proc、sysctl和sysfs

首先说这procfs和sysfs有什么区别呢?procfs不针对设备和设备驱动,而是针对整个内核信息的抽象接口。所以sysfs的出现就是为了独立出一个抽象接口来描述设备和驱动信息

另外,procfs和sysctl什么区别呢?procfs主要是用于展示内核信息(一般只读的),而sysctl虽然在/proc/sys下,但它主要是暴露一些内核参数,而用户也可以写入这个来动态修改

  • proc

以/proc/cpuinfo的过程倒推一下这个procfs的过程

#创建一个proc,第一个参数是名字,第二个参数是mode,第三个是父级,第四个cpuinfo_proc_ops必须是proc_ops结构体指针
#很明显,这个和sysfs差不多意思,NULL表示直接在/proc下创建proc,可以传入proc_dir_entry作为父级
#返回的也是一个proc_dir_entry结构体,也就是一个procfs目录
proc_create("cpuinfo", 0, NULL, &cpuinfo_proc_ops);

#比如这里就cat /proc/cpuinfo的话,就是调用open了
#open的话就是去调用cpuinfo_open?然后返回的就是一个seq_open的内容?
static int cpuinfo_open(struct inode *inode, struct file *file)
{
	return seq_open(file, &cpuinfo_op);
}

static const struct proc_ops cpuinfo_proc_ops = {
	.proc_flags	= PROC_ENTRY_PERMANENT,
	.proc_open	= cpuinfo_open,
	.proc_read_iter	= seq_read_iter,
	.proc_lseek	= seq_lseek,
	.proc_release	= seq_release,
};

#这个结构体初始化一些函数指针
const struct seq_operations cpuinfo_op = {
	.start	= c_start,
	.next	= c_next,
	.stop	= c_stop,
	.show	= show_cpuinfo,
};
#具体的实现
show_cpuinfo

如果想实现一个目录呢?就可以先搞一个proc_dir_entry,然后proc_mkdir创建

这块可以参照fs/dirty_pages.c中的内容,比较简短易读

  • sysctl

需要CONFIG_PROC_SYSCTL、CONFIG_SYSCTL的支持

原理:需要先在proc中注册/proc/sys,然后注册ctl_table这些

start_kernel
proc_root_init
proc_sys_init

int __init proc_sys_init(void)
{
	struct proc_dir_entry *proc_sys_root;

	proc_sys_root = proc_mkdir("sys", NULL);//创建目录
	proc_sys_root->proc_iops = &proc_sys_dir_operations;
	proc_sys_root->proc_dir_ops = &proc_sys_dir_file_operations;
	proc_sys_root->nlink = 0;

	return sysctl_init_bases();
}

int __init sysctl_init_bases(void)
{
	register_sysctl_init("kernel", kern_table);
	register_sysctl_init("vm", vm_table);

	return 0;
}

register_sysctl_init
__register_sysctl_init
register_sysctl_sz
__register_sysctl_table
#注册ctl_table

使用:其实只要在对应的ctl_table下加上对应的条目就好了,然后再去实现对应的handler的功能就好了

  • sysfs

sysfs目录下的各个子目录中存放的设备信息并非独立的,我们可以看成不同的目录是从不同的角度来描述某个设备信息;

这个基本过程应该就是:kobject_create_and_add创建一个目录,这个创建的是sysfs接口的目录,,,然后sysfs_create_group创建的这些kobj_attribute才是真正的sysfs文件,,,然后去实现对应的show store等函数。那么读取呢写入是怎么到show和store上的呢?通过VFS吧,读写sysfs本质上就是读文件

    1. 挂载

可以手动mount,比如mount -t sysfs sysfs /sys

或者/etc/fstab中有的也可能会有这个条目,但是我在现在的机器上没有找到,看来是自动搞到systemd中去了

    1. kobject对象(目录)

kobject可不只是给sysfs用的,还可以有其他的作用,引用计数、udev事件通知(netlink?)

相关内容在lib/kobject.c中

#这就是创建一个kobject/kset并加入到sysfs中,其实就是相当于创建一个sysfs目录
kobject_create_and_add
kset_create_and_add		##区别在于kset是kobject的集合,管理一组kobject

#kobject_create_and_add第二个参数是父指针,也就是指向他的父级目录,比如kernel_kobj这种的,其实都是kobject_create_and_add
#创建出来的,一堆这个创建出来,并指定父指针,就形成了一种层级结构
#kobject_create_and_add("X",NULL);传入null就是直接在/sys下创建,也就是最顶级的
    1. 属性

创建属性其实就是在目录下创建具体的文件了,他需要上一步中创建的kobject对象,主要在/include/linux/sysfs.h中

#这些宏很好理解,就是object结构体的赋值,传函数指针等
__ATTR
__ATTR_RO
__ATTR_WO
__ATTR_RW

#这就是attribute结构体,应该属于上层抽象了
#如果是创建sysfs结构体的话,因为是对应kobject结构体的,所以应该是一个kobj_attribute
struct attribute {
	const char		*name;
	umode_t			mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	bool			ignore_lockdep:1;
	struct lock_class_key	*key;
	struct lock_class_key	skey;
#endif
};

#这就是要使用__ATTR宏要定义的结构体的kobj_attribute类型
struct kobj_attribute {
	struct attribute attr;
	ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
			char *buf);
	ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
			 const char *buf, size_t count);
};

#其实这时候就可以写完了,定义好相关结构体后,kobject_create_and_add创建kobject,sysfs_create_file创建file
#后面就再去实现对应的show、store函数就行了

需要注意:kobj_attribute和kobject的这些声明要在show store这些函数的后面,因为得先有这些函数才能正确初始化

    1. 属性组

能够统一管理一些属性,比如同一目录下的一些属性统一管理的话,可以考虑搞到一个属性组里

struct attribute_group {
	const char		*name;
	umode_t			(*is_visible)(struct kobject *,
					      struct attribute *, int);
	umode_t			(*is_bin_visible)(struct kobject *,
						  struct bin_attribute *, int);
	struct attribute	**attrs;
	struct bin_attribute	**bin_attrs;
};

#用法示例
#像这个,先创建一个kobject(也就是目录),然后创建一个属性
#把属性挂到属性组里面
#接下来只要kobject_create_and_add创建这个kobject,然后sysfs_create_group创建属性组就好了
static struct kobj_attribute vm_cpumask_attr = __ATTR_RW(vm_cpumask);
static struct attribute *cpufreq_monitor_attrs[] = {
    &vm_cpumask_attr.attr,
    NULL,
};
static struct attribute_group cpufreq_monitor_group = {
    .attrs = cpufreq_monitor_attrs,
};

其他:

sysfs_emit:这个函数是用于简化sysfs中show的方法的函数,show没什么好特殊写的话,使用这个函数就可以,专门用于sysfs上下文


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 857879363@qq.com