读书人

施用 /sys 文件系统访问 Linux 内核

发布时间: 2013-03-17 13:48:31 作者: rapoo

使用 /sys 文件系统访问 Linux 内核

简介: sysfs 是 Linux 内核中设计较新的一种虚拟的基于内存的文件系统,它的作用与 proc 有些类似,但除了与 proc 相同的具有查看和设定内核参数功能之外,还有为 Linux 统一设备模型作为管理之用。相比于 proc 文件系统,使用 sysfs 导出内核数据的方式更为统一,并且组织的方式更好,它的设计从 proc 中吸取了很多教训。本文就 sysfs 的挂载点 /sys 目录结构、其与 Linux 统一设备模型的关系、常见属性文件的用法等方面对 sysfs 作入门介绍,并且就内核编程方面,以具体的例子来展示如何添加 sysfs 支持。

/sys 下的子目录所包含的内容/sys/devices这是内核对系统中所有设备的分层次表达模型,也是 /sys 文件系统管理设备的最重要的目录结构,下文会对它的内部结构作进一步分析;/sys/dev这个目录下维护一个按字符设备和块设备的主次号码(major:minor)链接到真实的设备(/sys/devices下)的符号链接文件,它是在内核 2.6.26 首次引入;/sys/bus这是内核设备按总线类型分层放置的目录结构, devices 中的所有设备都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接,它也是构成 Linux 统一设备模型的一部分;/sys/class这是按照设备功能分类的设备模型,如系统所有输入设备都会出现在 /sys/class/input 之下,而不论它们是以何种总线连接到系统。它也是构成 Linux 统一设备模型的一部分;/sys/block这里是系统中当前所有的块设备所在,按照功能来说放置在 /sys/class 之下会更合适,但只是由于历史遗留因素而一直存在于 /sys/block, 但从 2.6.22 开始就已标记为过时,只有在打开了 CONFIG_SYSFS_DEPRECATED 配置下编译才会有这个目录的存在,并且在 2.6.26 内核中已正式移到 /sys/class/block, 旧的接口 /sys/block 为了向后兼容保留存在,但其中的内容已经变为指向它们在 /sys/devices/ 中真实设备的符号链接文件;/sys/firmware这里是系统加载固件机制的对用户空间的接口,关于固件有专用于固件加载的一套API,在附录 LDD3 一书中有关于内核支持固件加载机制的更详细的介绍;/sys/fs这里按照设计是用于描述系统中所有文件系统,包括文件系统本身和按文件系统分类存放的已挂载点,但目前只有 fuse,gfs2 等少数文件系统支持 sysfs 接口,一些传统的虚拟文件系统(VFS)层次控制参数仍然在 sysctl (/proc/sys/fs) 接口中中;/sys/kernel这里是内核所有可调整参数的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的 slab 分配器等几项较新的设计在使用它,其它内核可调整参数仍然位于 sysctl (/proc/sys/kernel) 接口中 ;/sys/module这里有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件(vmlinuz)中还是编译为外部模块(ko文件),都可能会出现在/sys/module 中:编译为外部模块(ko文件)在加载后会出现对应的 /sys/module/<module_name>/, 并且在这个目录下会出现一些属性文件和属性目录来表示此外部模块的一些信息,如版本号、加载状态、所提供的驱动程序等;编译为内联方式的模块则只在当它有非0属性的模块参数时会出现对应的 /sys/module/<module_name>, 这些模块的可用参数会出现在 /sys/modules/<modname>/parameters/<param_name> 中,如 /sys/module/printk/parameters/time 这个可读写参数控制着内联模块 printk 在打印内核消息时是否加上时间前缀;所有内联模块的参数也可以由 "<module_name>.<param_name>=<value>" 的形式写在内核启动参数上,如启动内核时加上参数 "printk.time=1" 与 向 "/sys/module/printk/parameters/time" 写入1的效果相同;没有非0属性参数的内联模块不会出现于此。/sys/power这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。/sys/slab (对应 2.6.23 内核,在 2.6.24 以后移至 /sys/kernel/slab)从2.6.23 开始可以选择 SLAB 内存分配器的实现,并且新的 SLUB(Unqueued Slab Allocator)被设置为缺省值;如果编译了此选项,在 /sys 下就会出现 /sys/slab ,里面有每一个 kmem_cache 结构体的可调整参数。对应于旧的 SLAB 内存分配器下的 /proc/slabinfo 动态调整接口,新式的 /sys/kernel/slab/<slab_name> 接口中的各项信息和可调整项显得更为清晰。

接下来对 /sys/devices/ 下的目录结构作进一步探讨:


清单 2. 查看 /sys/devices/ 的目录结构

其中涉及到 ksets, kobjects, attrs 等很多术语,这就不得不提到 Linux 统一设备模型。

类型所包含的内容对应内核数据结构对应/sys项设备(Devices)设备是此模型中最基本的类型,以设备本身的连接按层次组织struct device/sys/devices/*/*/.../设备驱动(Device Drivers)在一个系统中安装多个相同设备,只需要一份驱动程序的支持struct device_driver/sys/bus/pci/drivers/*/总线类型(Bus Types)在整个总线级别对此总线上连接的所有设备进行管理struct bus_type/sys/bus/*/设备类别(Device Classes)这是按照功能进行分类组织的设备层次树;如 USB 接口和 PS/2 接口的鼠标都是输入设备,都会出现在 /sys/class/input/ 下struct class/sys/class/*/

从内核在实现它们时所使用的数据结构来说, Linux 统一设备模型又是以两种基本数据结构进行树型和链表型结构组织的:

kobject: 在 Linux 设备模型中最基本的对象,它的功能是提供引用计数和维持父子(parent)结构、平级(sibling)目录关系,上面的 device, device_driver 等各对象都是以 kobject 基础功能之上实现的;
ssize_t sculld_show_qset(struct device_driver *driver, char *buf){return snprintf(buf, PAGE_SIZE, "%d\n", sculld_qset);}ssize_t sculld_store_qset(struct device_driver *driver, const char *buf,size_t count){sculld_qset = simple_strtol(buf, NULL, 0);return count;}/* 声明一个权限为0644的可同时读写的driver属性 */static DRIVER_ATTR(qset, S_IRUGO | S_IWUSR, sculld_show_qset, sculld_store_qset);/* 创建此driver属性文件 */result = driver_create_file(&sculld_driver.driver, &driver_attr_qset);

驱动属性最终出现如 "/sys/bus/ldd/drivers/sculld/qset" ,这里声明的是同时可读写的,权限位 0644 与其保持一致。

6446 0 -rw-r--r-- 1 root root 4096 12月 14 07:44 /sys/bus/ldd/drivers/sculld/qset

小结

sysfs 给应用程序提供了统一访问设备的接口,但可以看到, sysfs 仅仅是提供了一个可以统一访问设备的框架,但究竟是否支持 sysfs 还需要各设备驱动程序的编程支持;在 2.6 内核诞生 5年以来的发展中,很多子系统、设备驱动程序逐渐转向了 sysfs 作为与用户空间友好的接口,但仍然也存在大量的代码还在使用旧的 proc 或虚拟字符设备的 ioctl 方式;如果仅从最终用户的角度来说, sysfs 与 proc 都是在提供相同或类似的功能,对于旧的 proc 代码,没有绝对的必要去做 proc 至 sysfs 的升级;因此在可预见的将来, sysfs 会与 proc, debugfs, configfs 等共存很长一段时间。


读书人网 >UNIXLINUX

热点推荐