• linux文件系统初探--Day3


    今天来看看day3中新增的代码。day3中并没有涉及太多一些文件系统原理上的知识,主要是对之前的代码进行补充与完善,添加了一些调试信息。主要涉及虚拟文件系统proc的一些知识,今天来学习一下。

    proc文件系统

    proc文件系统全名process data system,初衷是传递进程数据。

    proc文件系统使内核生成系统状态和相关配置的信息。这些信息可以有用户和系统程序从普通文件读取,无需专门工具与内核通信。

    该方法主要利用一个虚拟文件系统即时产生文件信息,即只有发出请求时,信息才会生成,而不是读取。此类虚拟文件系统并不需要专用的硬盘分区或者其他的块存储设备。

    proc文件系统中主要包含以下几类内容:

    1. 内存管理;
    2. 系统进程的特征数据;
    3. 文件系统;
    4. 设备驱动;
    5. 系统总线;
    6. 电源管理;
    7. 终端;
    8. 系统控制参数。
      其中我们主要关注文件系统的相关内容。

    源码分析

    /* helpful if this is different than other fs */
    #define SAMPLEFS_MAGIC     0x73616d70 /* "SAMP" */
    
    // module_param:内核态下传参的方法
    // module_param(name, type, privilege)
    unsigned int sample_parm = 0;
    module_param(sample_parm, int, 0);
    // MODULE_PARM_DESC:用来描述参数的宏。
    MODULE_PARM_DESC(sample_parm, "An example parm. Default: x Range: y to z");
    
    #ifdef CONFIG_PROC_FS
    static struct proc_dir_entry *proc_fs_samplefs;
    

    我们看到这里有一个结构体struct proc_dir_entry,该结构体表示proc文件系统中的数据项,每个数据项都由一个proc_dir_entry实例描述。

    struct proc_dir_entry {
    	/*
    	 * number of callers into module in progress;
    	 * negative -> it's going away RSN
    	 */
    	
            [---snipped---]
    	const struct inode_operations *proc_iops;
    	const struct file_operations *proc_fops;
    	const struct dentry_operations *proc_dops;
    	union {
    		const struct seq_operations *seq_ops;
    		int (*single_show)(struct seq_file *, void *);
    	};
    	proc_write_t write;      // 指向一个支持向内核写入的函数
    	void *data;      // 参数,不同的参数对应不同的操作
    	unsigned int state_size;
    	unsigned int low_ino;      // inode 编号
    	nlink_t nlink;      // 目录中子目录和符号链接的个数
    	kuid_t uid;      // 用户ID
    	kgid_t gid;      // 组ID
    	loff_t size;      // 文件大小(字节),因为proc数据项都是动态生成的,所以一般情况下size为0
    	struct proc_dir_entry *parent;      // 父目录指针
    	struct rb_root subdir;      // 指向一个目录中的第一个子数据项,有可能是文件,也可能是目录
    	struct rb_node subdir_node;
    	char *name;      // 文件名
    	umode_t mode;
    	u8 namelen;      // 文件名长度
    	char inline_name[];
    } __randomize_layout;
    

    sfs_proc_init

    void
    sfs_proc_init(void)
    {
    	/* proc_fs_samplefs = proc_mkdir("samplefs", proc_root_fs); */
            // 这里之前会在 fs/proc/root.c 的 proc_root_init 中创建 proc_root_fs = proc_mkdir("fs", NULL);
            proc_fs_samplefs = proc_mkdir("fs/samplefs", NULL);
    	if (proc_fs_samplefs == NULL)
    		return;
    
    	// proc_fs_samplefs->owner = THIS_MODULE;
    	// create_proc_read_entry("DebugData", 0, proc_fs_samplefs,
    				sfs_debug_read, NULL);
            // 创建
            proc_create("DebugData, 0, proc_fs_samplefs, &samplefs_proc_fops);
    }
    

    这个函数比较简单,主要是创建proc/fs/samplefs的目录和文件,主要的两个函数就是proc_mkdircreate_proc_read_entry。必须指出的是,通过代码来创建新的数据项并不常见,这些函数接口主要是用来测试,以便使用很小的代价在内核与用户空间打开一条通信渠道。

    proc_mkdir主要是创建新目录,create_proc_read_entry创建proc中的文件,但是这个功能在新内核中被替换为proc_create,以下是proc_create的定义。

    struct proc_dir_entry *proc_create(const char *name, umode_t mode,
    				   struct proc_dir_entry *parent,
    				   const struct file_operations *proc_fops)
    

    不难发现我们只需要填上struct file_operations这个参数即可。在老内核中,内核对proc文件的file_operations约束较为严格,实现了一个proc_file_operations:

    static struct file_operations proc_file_operations = {
          .llseek = proc_file_lseek,
          .read = proc_file_read,
          .write = proc_file_write,
    }
    

    所以只需要填上这几个函数即可。关于seq_file与proc的关系暂时不做详述,修改后的代码如下:

    static int samplefs_debug_open(struct seq_file *file, void *v)
    {
    	seq_printf(file, "===================Debug Info====================
    ");
    	return 0;
    }
    
    static int samplefs_proc_open(struct inode *inode, struct file *file)
    {
    	return single_open(file, samplefs_debug_open, NULL);
    }
    
    static const struct file_operations samplefs_proc_fops = {
    	.owner = THIS_MODULE,
    	.read = seq_read,
    	.llseek = seq_lseek,
    	//.write = seq_write,
    	.open = samplefs_proc_open,	
    	.release = seq_release,
    };
    

    关于proc文件写的问题暂时不在这里讨论。

    sfs_proc_clean

    这个函数比较简单,主要涉及remove_proc_entry这个函数,定义如下:

    void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
    

    所以也需要做一些小改动:

    void
    sfs_proc_clean(void)
    {
    	if (proc_fs_samplefs == NULL)
    		return;
    
    	remove_proc_entry("DebugData", proc_fs_samplefs);
    	remove_proc_entry("fs/samplefs", NULL);
    }
    #endif /* CONFIG_PROC_FS */
    
    static int __init init_samplefs_fs(void)
    {
    	printk(KERN_INFO "init samplefs
    ");
    #ifdef CONFIG_PROC_FS
    	sfs_proc_init();
    #endif
    
    	/* some filesystems pass optional parms at load time */
    	if (sample_parm > 256) {
    		printk(KERN_ERR "sample_parm %d too large, reset to 10
    ",
    			sample_parm);
    		sample_parm = 10;
    	}
    
    	return register_filesystem(&samplefs_fs_type);
    }
    
    static void __exit exit_samplefs_fs(void)
    {
    	printk(KERN_INFO "unloading samplefs
    ");
    #ifdef CONFIG_PROC_FS
    	sfs_proc_clean();
    #endif
    	unregister_filesystem(&samplefs_fs_type);
    }
    
    module_init(init_samplefs_fs)
    module_exit(exit_samplefs_fs)
    
    MODULE_LICENSE("GPL");
    

    剩下的代码没有改变。此时加载模块,dmesg会提示:

    解决办法在这里。大致意思是这是一个未经验证的模块,加载后污染内核,总体来说没什么影响。

    之后可以通过cat /proc/fs/samplefs/DebugData查看相关信息。这时就可以进行实验了!

    参考资料

    module_param的介绍
    module_param和MODULE_PARM_DESC
    proc文件系统介绍一
    proc文件系统与seq_file的读写

  • 相关阅读:
    java内存泄露
    hbase java api
    配置CRT远程登录
    kafka分区消费模型
    JAVA内存区域
    JVM分代和垃圾回收
    spring中bean的作用域
    分布式RPC
    session共享
    ZooKeeper实现分布式session
  • 原文地址:https://www.cnblogs.com/LuoboLiam/p/14314683.html
Copyright © 2020-2023  润新知