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====================\n");
	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\n");
#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\n",
			sample_parm);
		sample_parm = 10;
	}

	return register_filesystem(&samplefs_fs_type);
}

static void __exit exit_samplefs_fs(void)
{
	printk(KERN_INFO "unloading samplefs\n");
#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的读写

posted @ 2021-01-22 18:08  xinze  阅读(201)  评论(0编辑  收藏  举报