今天来看看day3中新增的代码。day3中并没有涉及太多一些文件系统原理上的知识,主要是对之前的代码进行补充与完善,添加了一些调试信息。主要涉及虚拟文件系统proc的一些知识,今天来学习一下。
proc文件系统
proc文件系统全名process data system,初衷是传递进程数据。
proc文件系统使内核生成系统状态和相关配置的信息。这些信息可以有用户和系统程序从普通文件读取,无需专门工具与内核通信。
该方法主要利用一个虚拟文件系统即时产生文件信息,即只有发出请求时,信息才会生成,而不是读取。此类虚拟文件系统并不需要专用的硬盘分区或者其他的块存储设备。
proc文件系统中主要包含以下几类内容:
- 内存管理;
- 系统进程的特征数据;
- 文件系统;
- 设备驱动;
- 系统总线;
- 电源管理;
- 终端;
- 系统控制参数。
其中我们主要关注文件系统的相关内容。
源码分析
/* 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_mkdir
和create_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的读写