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

来源:互联网 发布:逆淘汰 知乎 编辑:程序博客网 时间:2024/05/18 18:19

http://www.ibm.com/developerworks/cn/linux/l-cn-sysfs/

http://blog.csdn.net/qb_2008/article/details/6846412

sysfs 虚拟文件系统提供了一种比 proc 更为理想的访问内核数据的途径

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

添加 sysfs 支持

如果你正在开发的设备驱动程序中需要与用户层的接口,一般可选的方法有:

  1. 注册虚拟的字符设备文件,以这个虚拟设备上的 read/write/ioctl 等接口与用户交互;但 read/write 一般只能做一件事情, ioctl 可以根据 cmd 参数做多个功能,但其缺点是很明显的: ioctl 接口无法直接在 Shell 脚本中使用,为了使用 ioctl 的功能,还必须编写配套的 C语言的虚拟设备操作程序, ioctl 的二进制数据接口也是造成大小端问题 (big endian与little endian)、32位/64位不可移植问题的根源;
  2. 注册 proc 接口,接受用户的 read/write/ioctl 操作;同样的,一个 proc 项通常使用其 read/write/ioctl 接口,它所存在的问题与上面的虚拟字符设备的的问题相似;
  3. 注册 sysfs 属性;
  4. 最重要的是,添加虚拟字符设备支持和注册 proc 接口支持这两者所需要增加的代码量都并不少,最好的方法还是使用 sysfs 属性支持,一切在用户层是可见的透明,且增加的代码量是最少的,可维护性也最好;方法就是使用 <include/linux/device.h> 头文件提供的这四个宏,分别应用于总线/类别/驱动/设备四种内核数据结构对象上:

    #define BUS_ATTR(_name, _mode, _show, _store)   \struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)#define CLASS_ATTR(_name, _mode, _show, _store)                 \struct class_attribute class_attr_##_name = __ATTR(_name, _mode, _show, _store)#define DRIVER_ATTR(_name, _mode, _show, _store)        \struct driver_attribute driver_attr_##_name =           \        __ATTR(_name, _mode, _show, _store)#define DEVICE_ATTR(_name, _mode, _show, _store) \struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

    总线(BUS)和类别(CLASS)属性一般用于新设计的总线和新设计的类别,这两者一般是不用的;因为你的设备一般是以PCI等成熟的常规方式连接到主机,而不会去新发明一种类型;使用驱动属性和设备属性的区别就在于:看你的 sysfs 属性设计是针对整个驱动有效的还是针对这份驱动所可能支持的每个设备分别有效。

    从头文件中还可以找到 show/store 函数的原型,注意到它和虚拟字符设备或 proc 项的 read/write 的作用很类似,但有一点不同是 show/store 函数上的 buf/count 参数是在 sysfs 层已作了用户区/内核区的内存复制,虚拟字符设备上常见的 __user 属性在这里并不需要,因而也不需要多一次 copy_from_user/copy_to_user, 在 show/store 函数参数上的 buf/count 参数已经是内核区的地址,可以直接操作。

    上面四种都是 Linux 统一设备模型所添加的高级接口,如果使用 sysfs 所提供的底层接口的话,则还有下面两个,定义来自 <include/linux/sysfs.h> :(上面的总线/类别/驱动/设备四个接口都是以这里的__ATTR实现的)

    #define __ATTR(_name,_mode,_show,_store) { \        .attr = {.name = __stringify(_name), .mode = _mode },   \        .show   = _show,                                        \.store  = _store,                                       \}#define __ATTR_RO(_name) { \        .attr   = { .name = __stringify(_name), .mode = 0444 }, \        .show   = _name##_show,                                 \}

    上面这些宏都是在注册总线/类别/驱动/设备时作为缺省属性而使用的,在实际应用中还有一种情况是根据条件动态添加属性,如 PCI 设备上的 resource{0,1,2,...} 属性文件,因为一个 PCI 设备上的可映射资源究竟有多少无法预知,也只能以条件判断的方式动态添加上。

    int __must_check sysfs_create_file(struct kobject *kobj,                                   const struct attribute *attr);int __must_check sysfs_create_bin_file(struct kobject *kobj,                                       struct bin_attribute *attr);

    这两个函数可以对一个 kobject 动态添加上文本属性或二进制属性,这也是唯一可以添加二进制属性的方法。

    二进制属性与普通文本属性的区别在于:

    • 二进制属性 struct bin_attribute 中内嵌一个 struct attribute 结构体对象,因此具有普通属性的所有功能特征;
    • 二进制属性上多一个 size 用来描述此二进制文件的大小,而普通属性文件的大小总是 4096, 准确地说,应该是一个内存页的大小,因为从当前 sysfs 内核实现来说,它分配一个内存页面来作为 (buf/count) 的缓冲区;
    • 二进制属性比普通属性多内存映射(mmap)接口的支持;
原创粉丝点击