sysfs是一个基于RAM的文件系统,它和kobject一起,可以将Kernel的数据结构导出到用户空间,以文件目录结构的形式,提供对这些数据结构(以及数据结构的属性)的访问支持。
sysfs目录 |
所包含内容 |
/sys/devices |
这是内核对系统中所有设备的分层次表达模型,也是 /sys 文件系统管理设备的最重要的目录结构 |
/sys/dev |
这个目录下维护一个按字符设备和块设备的主次号码(major:minor)链接到真实的设备(/sys/devices下)的符号链接文件 |
/sys/bus |
这是内核设备按总线类型分层放置的目录结构, devices 中的所有设备都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接,它也是构成Linux统一设备模型的一部分; |
/sys/class |
这是按照设备功能分类的设备模型,如系统所有输入设备都会出现在 /sys/class/input之下,而不论它们是以何种总线连接到系统。它也是构成Linux统一设备模型的一部分; |
/sys/block(stale) |
这里是系统中当前所有的块设备所在,按照功能来说放置在 /sys/class 之下会更合适,但只是由于历史遗留因素而一直存在于/sys/block,但从2.6.22开始就已标记为过时,只有在打开了CONFIG_SYSFS_DEPRECATED配置下编译才会有这个目录的存在,并且在2.6.26内核中已正式移到/sys/class/block,旧的接口/sys/block为了向后兼容保留存在,但其中的内容已经变为指向它们在/sys/devices/中真实设备的符号链接文件; |
/sys/firmware |
这里是系统加载固件机制的对用户空间的接口,关于固件有专用于固件加载的一套API |
/sys/fs |
这里按照设计是用于描述系统中所有文件系统,包括文件系统本身和按文件系统分类存放的已挂载点; |
/sys/kernel |
这里是内核所有可调整参数的位置; |
/sys/module |
这里有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件(vmlinuz)中还是编译为外部模块(ko文件),都可能会出现在/sys/module中: 编译为外部模块(ko文件)在加载后会出现对应的/sys/module/ 编译为内联方式的模块则只在当它有非0属性的模块参数时会出现对应的/sys/module/ 如 /sys/module/printk/parameters/time 这个可读写参数控制着内联模块printk在打印内核消息时是否加上时间前缀; 所有内联模块的参数也可以由 " 没有非0属性参数的内联模块不会出现于此。 |
/sys/power |
这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。 |
前面已经讲过sysfs和kobject等的关系了,这里不再赘述,下面主要说一下属性和sysfs与普通文件系统的关系。
前面已经看到的跟属性相关的关键数据结构有:
l attribute
l bin_attribute
l attribute_group
前面讲过,属性对应sysfs中的一个文件,那么在sysfs中,为什么会有attribute的概念呢?sysfs中的目录描述了kobject,而kobject是特定数据类型变量(如struct device)的体现。因此kobject的属性,就是这些变量的属性。它可以是任何东西,名称、一个内部变量、一个字符串等等。而attribute,在sysfs文件系统中是以文件的形式提供的,即:kobject的所有属性,都在它对应的sysfs目录下以文件的形式呈现。这些文件一般是可读、写的,而kernel中定义了这些属性的模块,会根据用户空间的读写操作,记录和返回这些attribute的值。
总之,所谓的attibute,就是内核空间和用户空间进行信息交互的一种方法。例如某个driver定义了一个变量,却希望用户空间程序可以修改该变量,以控制driver的运行行为,那么就可以将该变量以sysfs attribute的形式开放出来。
属性分为普通属性和二进制属性
1.普通属性
name,属性的名字,对应sysfs中文件的名字。
mode,应用于属性(文件)的保护位,与文件的权限相同。
2.二进制属性
attr,包含的普通属性。
size,二进制属性的最大长度,如果没有最大值可以设为0。
private,私有数据,与linux中大部分私有数据的用途一样,用于传递私有数据结构。
read()、write()、mmap(),操作二进制属性的函数。
二进制属性不能作为默认属性被设置,只能显式地创建。
使用该attribute生成的sysfs文件,只能用字符串的形式读写。而struct bin_attribute在struct attribute的基础上,增加了read、write等函数,因此它所生成的sysfs文件可以用任何方式读写。固件一般使用bin_attribute属性。
3.属性组
name,属性组的名字,不为空的话对应一个sysfs的文件夹。
attrs,属性组里的普通属性列表。
bin_attrs,属性组里的二进制属性列表。
is_visible(),返回组中属性的读写权限。
属性通过sysfs_ops进行读写。
show,显示属性值。
store,保存属性值。
sysfs中的目录和文件与kobject和attribute对应,而普通文件系统是file和inode。sysfs在上层也是通过普通的read(),write()等系统调用进行操作的,那么需要将file_operations转换成最终的show()/store()。怎么转?因为属性都是属于kobject的,所以最后的读写肯定与kobject有关,先从kobject找找线索,看前面kobject有个属性——sd,kernfs_node数据结构,是kobject在sysfs的表示。我们从分析这个核心数据结构入手。
count、active,相关的计数,原子的。
parent,本节点的父节点,这个比较重要,属性是文件,父节点才是kobject。
name,节点名字。
rb,红黑树节点。
ns、has,命名空间相关。
dir、symlink、attr,节点的类型,表示该节点是目录、符号链接还是属性。三个属性定义在联合体中,我们在这关注是的attr。
priv,私有数据,看到私有数据应该推测,有用的东西就在这里传递,事实也是如此,在本节描述的访问属性的场景下,kobject就作为私有数据随kernfs_node传递。
flags、mode,与文件的相关属性含义一致。
ino,推测是子设备号。
iattr,节点本身的属性。
那么file如何转换成kernfs_node的呢,类比一下,普通文件有file和inode,这里kernfs_node应该是相当于inode,那还应该有代表文件的结构与之对应,没错,是有这个结构。这个结构是什么,不妨先在kernfs_node中找线索。既然我们的研究对象是属性,那就看看联合里的attr对应的数据结构。
ops,对于kernfs的操作函数。
open,打开的kernfs_node。
size,没有查到用途,不影响分析。
notify_next,通知kernfs文件,具体功能不详,不影响分析。
只关注ops,目前linux中基本所有的操作都独立抽象出了数据结构,这个数据结构应该是操作kernfs的函数,可能与我们找的答案有关。
atomic_write_len,写是以kernel page为边界的,如果没有设置atomic_write_len,超过PAGE_SIZE的写操作会被分成几个操作进行。如果设置了atomic_write_len,那么超过这个设置值的写操作,atomic_write_len以内的部分是原子写,超过的部分直接返回-E2BIG。
prealloc,设置了用mmap,不设置用read/write。因为read/wirte有自己的buf,与prealloc的buf不兼容。
seq_show、seq_start、seq_next、seq_stop,顺序文件操作。下面会详细分析。这里需要关注他们的参数。
read、write、mmap,读,写,内存映射函数,这里关注他们的参数。
看一下seq_xx和read的原型,参数分别为seq_file和kernfs_open_file。从名字上看kernfs_open_file和kernfs_node的关系,很像file和inode的关系。不妨先看看kernfs_open_file。
kn,所属的目录节点。
file,代表打开文件。
priv,私有数据结构。
mutex,互斥体。
list,确实没有查到在哪里调用,暂时推测不出用途,不影响分析。
pralloc_buf,mmap使用。
atomic_write_len,同kernfs_elem_attr。
mmaped,是否已经进行了ioremap。
vm_ops,虚拟内存管理操作。
从核心类的成员可能得出这样的推测,kernfs_open_file.file存储普通文件的file指针,在进行读写是用container_of()从file里获得kernfs_open_file结构体。以前的内核可能是这么做的,但是现在不是这样的。这里要提到上面数的另一个核心数据结构seq_file。
seq_file是为proc文件系统设计的,来历是这样的,由于procfs的默认操作函数只使用一页的缓存,在处理较大的proc文件时就有点麻烦,并且在输出一系列结构体中的数据时也比较不灵活,需要自己在read_proc函数中实现迭代,容易出现Bug。所以内核黑客们对一些/proc代码做了研究,抽象出共性,最终形成了seq_file(Sequence file:序列文件)接口。 这个接口提供了一套简单的函数来解决以上proc接口编程时存在的问题,使得编程更加容易,降低了Bug出现的机会。
不只是proc,在需要创建一个由一系列数据顺序组合而成的虚拟文件或一个较大的虚拟文件时,推荐使用seq_file接口。这正是符合bin_attribute的场景。看一下seq_file的结构。
这个结构的成员与file差不多,基本不用关心,因为他们都是使用op成员定义的方法操作的,一定意义上可以看作私有成员,图中没有这么画。
需要注意的是结构里有个private,这么多回了应该有这个意识了,看见private就有可能跟我们结构挂上关系。事实也是如此。kernfs_open_file的指针是放到seq_file的private中的。
至此只剩seq_file如何与普通的file联系在一起了,那就简单了,跟大部分驱动程序是一样的,seq_file包含在file的private_data中。整合上述关系,可以在下面类图中表示。
从file找到sysfs的属性文件,经历了很长的链条,这个其实是多态,由于c语言的特性,linux驱动程序用了很费劲的方式实现了多态。
上边这个可能还是抽象一点,把ops族对象加入跟为直观,忽略一些中间环节。
从操作上,从VFS的fs到sysfs经历了file_operations域,kernfs_ops域和最终的sys_ops域的转换。