/proc 文件系统的作用
/proc 文件系统本来目的是提供关于系统中运行的进程的信息,到后来成为了用户空间与内核进行通信的一种手段。
可以参考文章:
Linux内核通信之—proc文件系统(详解) - 逝去的浪花 - CSDN博客
这里自己写一个模块理解 /proc 文件系统 的使用,实现 cat /proc/my_proc/verison 可以查看模块版本信息。
/proc 文件的创建
proc_mkdir 接口用于创建目录,第二个参数为父目录,传NULL表示在/proc 下创建
proc_create 创建文件,需绑定文件操作函数,跟字符设备一样
static int __init procfs_init(void)
{
my_proc_root = proc_mkdir("my_proc",NULL); // 创建 /proc/my_proc
if(!my_proc_root)
{
debug();
return -1;
}
my_proc_ver = proc_create("version", 644,my_proc_root,&my_proc_fops);
//创建/proc/my_proc/version
if(!my_proc_ver)
{
debug();
return -1;
}
return 0;
}
my_proc_fops 的两种写法
写法一:
/*
只读:写法一
*/
struct file_operations my_proc_fops=
{
.owner = THIS_MODULE,
.read = my_proc_read,
//.write = my_proc_write,
};
static ssize_t my_proc_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos)
{
ssize_t retval = 0;
char *pver = "V1.0.1";
if(0 != *f_pos) //第二次读时返回0,否则cat 时会一直打印V1.0.1 不停
{
return 0;
}
if(copy_to_user(buf,"V1.0.1",strlen(pver)+1))
{
debug();
retval = -EIO;
}
else
{
retval = strlen(pver)+1;
*f_pos += retval;
}
return retval;
}
该写法简单,好理解,直接使用 copy_to_user 传给用户空间。
但是在内核代码中,看到更多的是使用seq_file 的 single_open 接口。
写法二:
static int my_proc_seq_show(struct seq_file *sfile, void *data)
{
char *pver = "V1.0.1";
seq_printf(sfile, "%s\n",pver);
return 0;
}
static int my_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, my_proc_seq_show, PDE_DATA(inode));
}
/*
只读:写法二
*/
struct file_operations my_proc_fops_2=
{
.owner = THIS_MODULE,
.open = my_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release, // 使用single_open 时必须是 single_release
};
使用single_open 只需要实现 seq_show接口 作为single_open的第二个参数。
seq_file 接口理解
作为一种清理 /proc 代码以及使内核开发者活得轻松些的方法, 添加了 seq_file 接口. 这个接口提供了简单的一套函数来实现大的内核虚拟文件。[引用自LDDR3 第四章4.3]
struct seq_operations {
void * (*start) (struct seq_file *m, loff_t *pos);
void (*stop) (struct seq_file *m, void *v);
void * (*next) (struct seq_file *m, void *v, loff_t *pos);
int (*show) (struct seq_file *m, void *v);
};
完整的seq_file 需要是实现 start ,stop , next,show 四个基本方法。
single_open 只使用了show 方法,start ,stop , next 这三个用的是默认接口single_xxx 。
int single_open(struct file *file, int (*show)(struct seq_file *, void *),
void *data)
{
struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
int res = -ENOMEM;
if (op) {
op->start = single_start;
op->next = single_next;
op->stop = single_stop;
op->show = show;
res = seq_open(file, op);
if (!res)
((struct seq_file *)file->private_data)->private = data;
else
kfree(op);
}
return res;
}
start ,stop , next,show 四个基本方法 其实最终被 seq_read 函数调用。
调用顺序大致如下:(参考代码 linux-4.14.63\fs\seq_file.c)
start->show->next->…->stop
single_open 只会调用一次show。
学习总结:
对于输出信息少的情况,两种写法其实都可以,只是写法二看上去更为规范。
另外不建议在/proc下创建文件。
建议新代码中获取内核信息是利用 sysfs