zoukankan      html  css  js  c++  java
  • linux文件系统初探--Day1

    2021年开新坑!理论上2月7号就可以完结了吧,大概。。。

    本系列文章参考资料:Linux Filesystems in 21 days

    本系列文章目的是创建一个samplefs,一个足够小的,容易理解的文件系统。据作者所说,samplefs是在ramfs和rkfs的基础上的扩展。

    实验环境:linux-5.4.89 Ubuntu-18.04-amd64

    DAY1

    一个Linux文件系统的内核驱动可以直接在编译内核时编到内核镜像中,也可以作为一个单独的模块,在需要时动态加载。

    一个新的Linux文件系统通常需要修改或新增以下几个位置:

    1. Kconfig中的条目(./fs/Kconfig),一般在make menuconfig之后可以选择;
    2. 新的目录(./fs/samplefs)
    3. Makefile文件(./fs/samplefs/Makefile)
    4. 组件初始化、移除时的C代码

    首先,先看samplefs/day1中的代码:

    /* samplefs/day1/super.c */
    
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/version.h>
    
    /* helpful if this is different than other fs */
    #define SAMPLEFS_MAGIC     0x73616d70 /* "SAMP" */
    
    static int samplefs_fill_super(struct super_block * sb, void * data, int silent)
    {
    	return 0;
    }
    
    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
    struct super_block * samplefs_get_sb(struct file_system_type *fs_type,
            int flags, const char *dev_name, void *data)
    {
    	return get_sb_nodev(fs_type, flags, data, samplefs_fill_super);
    }
    #else
    int samplefs_get_sb(struct file_system_type *fs_type,
            int flags, const char *dev_name, void *data, struct vfsmount *mnt)
    {
    	return get_sb_nodev(fs_type, flags, data, samplefs_fill_super, mnt);
    }
    #endif
    
    
    static struct file_system_type samplefs_fs_type = {
    	.owner = THIS_MODULE,
    	.name = "samplefs",
    	.get_sb = samplefs_get_sb,
    	.kill_sb = kill_anon_super,
    	/*  .fs_flags */
    };
    
    
    static int __init init_samplefs_fs(void)
    {
    	return register_filesystem(&samplefs_fs_type);
    }
    
    static void __exit exit_samplefs_fs(void)
    {
    	unregister_filesystem(&samplefs_fs_type);
    }
    
    module_init(init_samplefs_fs)
    module_exit(exit_samplefs_fs)
    

    如前所述,注册文件系统时,文件系统可以直接编译到内核中,也可以编译为模块。那么我们首先需要做的,就是向内核声明我们的文件系统的一些基本信息,这些信息内核通过file_system_type进行维护。

    struct file_system_type {
    	const char *name; //保存文件系统名称
    	int fs_flags; //使用的标志,
    #define FS_REQUIRES_DEV		1 
    #define FS_BINARY_MOUNTDATA	2
    #define FS_HAS_SUBTYPE		4
    #define FS_USERNS_MOUNT		8	/* Can be mounted by userns root */
    #define FS_RENAME_DOES_D_MOVE	32768	/* FS will handle d_move() during rename() internally. */
    	int (*init_fs_context)(struct fs_context *);
    	const struct fs_parameter_description *parameters;
    	struct dentry *(*mount) (struct file_system_type *, int,
    		       const char *, void *);
    	void (*kill_sb) (struct super_block *); //清理不再使用时的文件系统的超级块
    	struct module *owner; //指向module结构的指针
    	struct file_system_type * next;
    	struct hlist_head fs_supers; //一个文件系统会对应多个超级块,通常使用链表维护,fs_supers表示链表的表头。
    

    这里未展示file_system_type关于锁部分的成员。

    由于代码时间十分久远,内核API也经过了大量的修改,主要的修改涉及到file_system_type中的get_sb成员函数,可能在某个版本之后,file_system_type进行了一定程度上的修改,转而使用mount成员函数完成装载的操作。因此需要对super.c进行如下的修改:

    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/version.h>
    
    /* helpful if this is different than other fs */
    #define SAMPLEFS_MAGIC     0x73616d70 /* "SAMP" */
    
    static int samplefs_fill_super(struct super_block * sb, void * data, int silent)
    {
    	return 0;
    }
    
    struct dentry *samplefs_mount(struct file_system_type *fs_type, 
    	int flags, const char *dev_name, void *data)
    {
    	return mount_nodev(fs_type, flags, data, samplefs_fill_super);
    }
    
    static struct file_system_type samplefs_fs_type = {
    	.owner = THIS_MODULE,
    	.name = "samplefs",
    	.mount = samplefs_mount,
    	.kill_sb = kill_anon_super,
    	/*  .fs_flags */
    };
    
    static int __init init_samplefs_fs(void)
    {
    	return register_filesystem(&samplefs_fs_type);
    }
    
    static void __exit exit_samplefs_fs(void)
    {
    	unregister_filesystem(&samplefs_fs_type);
    }
    
    module_init(init_samplefs_fs)
    module_exit(exit_samplefs_fs)
    MODULE_LICENSE("GPL");
    

    我们看到,修改部分涉及内核的mount过程,因此mount流程是接下来需要分析的流程。

    那么接下来就到了熟悉的编内核阶段,要进行如下修改:

    第一步,将源代码文件夹复制到./fs下;

    第二步,在./fs/Kconfig末尾添加source "fs/samplefs/Kconfig"

    source "fs/nls/Kconfig"
    source "fs/dlm/Kconfig"
    source "fs/unicode/Kconfig"
    source "fs/samplefs/Kconfig"
    
    endmenu
    

    第三步,修改./fs/samplefs/Kconfig.diff,并重命名Kconfig.diff为Kconfig:

    config SAMPLEFS_FS
            tristate "Sample filesystem (EXPERIMENTAL)"
            help
              Samplefs is a sample filesystem for learning how to build
              a simple Linux filesystem
    
              If unsure, say N.
    
    config SAMPLEFS_DEBUG
    	bool "Additional debugging statements for samplefs"
    	depends on SAMPLEFS_FS
    	help
               Enabling this option adds a few more debugging routines
               to the samplefs code which slightly increases the size of
               the samplefs module and can cause additional logging of debug
               messages in some error paths, slowing performance. This
               option can be turned off unless you are debugging
               samplefs problems.  If unsure, say N.
    

    最后,内核编译老一套:

    sudo make menuconfig
    sudo make 
    sudo make modules_install
    sudo make install
    

    其中在menuconfig时,在出现的图形化界面中选择File System下的最后,选择samplefs为模块编译。

    完成后重启,选择5.4.89内核启动,此时sudo make M=[linux src path]/fs/samplefs/day1,之后sudo insmod samplefs.ko,可以看到samplefs加载成功。

    参考资料

    Linux文件系统基础(2)
    上个链接的github
    从ramfs分析文件系统的设计和实现
    深入Linux内核架构·第八章·8.4 处理VFS对象(本书的代码版本比较老)

  • 相关阅读:
    linkedLoop
    loopqueue
    expect 切换用户
    二叉树的实现
    栈的链表实现, 底层使用链表
    栈的数组实现
    RSA加密算法
    输入一个链表,反转链表后,输出链表的所有元素
    输入一个链表,输出该链表中倒数第k个结点
    ansible中include_tasks和import_tasks
  • 原文地址:https://www.cnblogs.com/LuoboLiam/p/14289956.html
Copyright © 2011-2022 走看看