linux /proc 文件系统学习

/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

发布了63 篇原创文章 · 获赞 20 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/agave7/article/details/94620078