经过长时间学习Linux统一设备,于是和大家分享一下,看完本文你肯定有不少收获,希望本文能教会你更多东西。在 Linux 2.5 内核的开发过程中,人们设计了一套新的设备模型,目的是为了对计算机上的所有Linux统一设备进行统一地表示和操作,包括Linux统一设备本身和设备之间的连接关系。
这个模型是在分析了 PCI 和 USB 的总线驱动过程中得到的,这两个总线类型能代表当前系统中的大多数设备类型,它们都有完善的热挺拔机制和电源管理的支持,也都有级连机制的支持,以桥接的 PCI/USB 总线控制器的方式可以支持更多的 PCI/USB 设备。为了给所有设备添加统一的电源管理的支持,而不是让每个Linux统一设备中去独立实现电源管理的支持,人们考虑的是如何尽可能地重用代码;而且在有层次模型的 PCI/USB 总线中,必须以合理形式展示出这个层次关系,这也是电源管理等所要求的必须有层次结构。
如在一个典型的 PC 系统中,中央处理器(CPU)能直接控制的是 PCI 总线设备,而 USB 总线设备是以一个 PCI 设备(PCI-USB桥)的形式接入在 PCI 总线设备上,外部 USB 设备再接入在 USB 总线设备上;当计算机执行挂起(suspend)操作时, Linux 内核应该以 “外部USB设备->USB总线设备->PCI总线设备” 的顺序通知每一个设备将电源挂起;执行恢复(resume)时则以相反的顺序通知;反之如果不按此顺序则将有设备得不到正确的电源状态变迁的通知,将无法正常工作。
sysfs 是在这个 Linux统一设备模型的开发过程中的一项副产品(见 参考资料 中 Greg K. Hartman 写作的 LinuxJournal 文章)。为了将这些有层次结构的设备以用户程序可见的方式表达出来,人们很自然想到了利用文件系统的目录树结构(这是以 UNIX 方式思考问题的基础,一切都是文件!)在这个模型中,有几种基本类型它们的对应关系。
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 基础功能之上实现的; struct kobject {
const char *name;
struct list_headentry;
struct kobject *parent;
struct kset *kset;
struct kobj_type*ktype;
struct sysfs_dirent *sd;
struct kref kref;
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
其中 struct kref 内含一个 atomic_t 类型用于引用计数, parent 是单个指向父节点的指针, entry 用于父 kset 以链表头结构将 kobject 结构维护成双向链表;
kset: 它用来对同类型对象提供一个包装集合,在内核数据结构上它也是由内嵌一个 kboject 实现,因而它同时也是一个 kobject (面向对象 OOP 概念中的继承关系) ,具有 kobject 的全部功能; struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
struct kset_uevent_ops *uevent_ops;
其中的 struct list_head list 用于将集合中的 kobject 按 struct list_head entry 维护成双向链表;
涉及到文件系统实现来说, sysfs 是一种基于 ramfs 实现的内存文件系统,与其它同样以 ramfs 实现的内存文件系统(configfs,debugfs,tmpfs,...)类似, sysfs 也是直接以 VFS 中的 struct inode 和 struct dentry 等 VFS 层次的结构体直接实现文件系统中的各种对象;同时在每个文件系统的私有数据 (如 dentry->d_fsdata 等位置) 上,使用了称为 struct sysfs_dirent 的结构用于表示 /sys 中的每一个目录项。
struct sysfs_dirent {
atomic_ts_count;
atomic_ts_active;
struct sysfs_dirent *s_parent;
struct sysfs_dirent *s_sibling;
const char *s_name;
union {
struct sysfs_elem_dir s_dir;
struct sysfs_elem_symlink s_symlink;
struct sysfs_elem_attr s_attr;
struct sysfs_elem_bin_attr s_bin_attr;
unsigned int s_flags;
ino_t s_ino;
umode_t s_mode;
struct iattr *s_iattr;
在上面的 kobject 对象中可以看到有向 sysfs_dirent 的指针,因此在sysfs中是用同一种 struct sysfs_dirent 来Linux统一设备模型中的 kset/kobject/attr/attr_group.
具体在数据结构成员上, sysfs_dirent 上有一个 union 共用体包含四种不同的结构,分别是目录、符号链接文件、属性文件、二进制属性文件;其中目录类型可以对应 kobject,在相应的 s_dir 中也有对 kobject 的指针,因此在内核数据结构, kobject 与 sysfs_dirent 是互相引用的;
sysfs 所表达的 /sys 目录结构就是非常清晰明了:
在 /sys 根目录之下的都是 kset,它们组织了 /sys 的顶层目录视图;
在部分 kset 下有二级或更深层次的 kset;
每个 kset 目录下再包含着一个或多个 kobject,这表示一个集合所包含的 kobject 结构体;
在 kobject 下有属性(attrs)文件和属性组(attr_group),属性组就是组织属性的一个目录,它们一起向用户层提供了表示和操作这个 kobject 的属性特征的接口;
在 kobject 下还有一些符号链接文件,指向其它的 kobject,这些符号链接文件用于组织上面所说的 device, driver, bus_type, class, module 之间的关系;
不同类型如设备类型的、设备驱动类型的 kobject 都有不同的属性,不同驱动程序支持的 sysfs 接口也有不同的属性文件;而相同类型的设备上有很多相同的属性文件;
注意,此表内容是按照***开发中的 2.6.28 内核的更新组织的,在附录资源如 LDD3 等位置中有提到 sysfs 中曾有一种管理对象称为 subsys (子系统对象),在***的内核中经过重构认为它是不需要的,它的功能完全可以由 kset 代替,也就是说 sysfs 中只需要一种管理结构是 kset,一种代表具体对象的结构是 kobject,在 kobject 下再用属性文件表示这个对象所具有的属性。以上是介绍Linux统一设备。
【编辑推荐】