1、Linux 2.6 内核的一个重要特色是提供了统一的内核设备模型。随着技术的不断进步,系统的拓扑结构越来越复杂,对智能电源管理、热插拔以及 plug and play 的支持要求也越来越高,2.4 内核已经难以满足这些需求。为适应这种形势的需要,2.6 内核开发了全新的设备模型。 Sysfs 文件系统Sysfs 文件系统是一个类似于 proc 文件系统的特殊文件系统,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息。其顶层目录主要有:Block 目录:包含所有的块设备Devices 目录:包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构Bus 目录:包含系
2、统中所有的总线类型Drivers 目录:包括内核中所有已注册的设备驱动程序Class 目录:系统中的设备类型(如网卡设备,声卡设备等) 内核对象机制关键数据结构2.1 kobject 内核对象Kobject 是 Linux 2.6 引入的新的设备管理机制,在内核中由 struct kobject 表示。通过这个数据结构使所有设备在底层都具有统一的接口,kobject 提供基本的对象管理,是构成Linux 2.6 设备模型的核心结构,它与 sysfs 文件系统紧密关联,每个在内核中注册的kobject 对象都对应于 sysfs 文件系统中的一个目录。Kobject 结构定义为:struct ko
3、bject char * k_name; 指向设备名称的指针char nameKOBJ_NAME_LEN; 设备名称struct kref kref; 对象引用计数struct list_head entry; 挂接到所在 kset 中去的单元struct kobject * parent; 指向父对象的指针struct kset * kset; 所属 kset 的指针struct kobj_type * ktype; 指向其对象类型描述符的指针struct dentry * dentry; sysfs 文件系统中与该对象对应的文件节点路径指针;其中的 kref 域表示该对象引用的计数,内核通
4、过 kref 实现对象引用计数管理,内核提供两个函数 kobject_get()、kobject_put() 分别用于增加和减少引用计数,当引用计数为 0 时,所有该对象使用的资源将被释放。Ktype 域是一个指向 kobj_type 结构的指针,表示该对象的类型。Kobj_type 数据结构包含三个域:一个 release 方法用于释放 kobject 占用的资源;一个 sysfs_ops 指针指向 sysfs 操作表和一个 sysfs 文件系统缺省属性列表。Sysfs 操作表包括两个函数 store()和 show()。当用户态读取属性时,show()函数被调用,该函数编码指定属性值存入
5、buffer 中返回给用户态;而 store()函数用于存储用户态传入的属性值。2.2 kset 内核对象集合Kobject 通常通过 kset 组织成层次化的结构, kset 是具有相同类型的 kobject 的集合,在内核中用 kset 数据结构表示,定义为:struct kset struct subsystem * subsys; 所在的 subsystem 的指针struct kobj_type * ktype; 指向该 kset 对象类型描述符的指针struct list_head list; 用于连接该 kset 中所有 kobject 的链表头struct kobject ko
6、bj; 嵌入的 kobjectstruct kset_hotplug_ops * hotplug_ops; 指向热插拔操作表的指针;包含在 kset 中的所有 kobject 被组织成一个双向循环链表,list 域正是该链表的头。Ktype域指向一个 kobj_type 结构,被该 kset 中的所有 kobject 共享,表示这些对象的类型。Kset数据结构还内嵌了一个 kobject 对象(由 kobj 域表示) ,所有属于这个 kset 的 kobject 对象的 parent 域均指向这个内嵌的对象。此外,kset 还依赖于 kobj 维护引用计数:kset 的引用计数实际上就是内嵌的
7、 kobject 对象的引用计数。2.3 subsystem 内核对象子系统Subsystem 是一系列 kset 的集合,描述系统中某一类设备子系统,如 block_subsys 表示所有的块设备,对应于 sysfs 文件系统中的 block 目录。类似的,devices_subsys 对应于 sysfs中的 devices 目录,描述系统中所有的设备。Subsystem 由 struct subsystem 数据结构描述,定义为:struct subsystem struct kset kset; 内嵌的 kset 对象struct rw_semaphore rwsem; 互斥访问信号量;
8、每个 kset 必须属于某个 subsystem,通过设置 kset 结构中的 subsys 域指向指定的 subsystem可以将一个 kset 加入到该 subsystem。所有挂接到同一 subsystem 的 kset 共享同一个 rwsem信号量,用于同步访问 kset 中的链表。 内核对象机制主要相关函数针对内核对象不同层次的数据结构,linux 2.6 内核定义了一系列操作函数,定义于lib/kobject.c 文件中。3.1 kobject 相关函数void kobject_init(struct kobject * kobj);kobject 初始化函数。设置 kobject
9、 引用计数为,entry 域指向自身,其所属 kset 引用计数加。int kobject_set_name(struct kobject *kobj, const char *format, .);设置指定 kobject 的名称。void kobject_cleanup(struct kobject * kobj)和 void kobject_release(struct kref *kref);kobject 清除函数。当其引用计数为时,释放对象占用的资源。struct kobject *kobject_get(struct kobject *kobj);将 kobj 对象的引用计数加
10、1,同时返回该对象的指针。void kobject_put(struct kobject * kobj);将 kobj 对象的引用计数减 1,如果引用计数降为 0,则调用 kobject_release()释放该 kobject对象。int kobject_add(struct kobject * kobj);将 kobj 对象加入 Linux 设备层次。挂接该 kobject 对象到 kset 的 list 链中,增加父目录各级kobject 的引用计数,在其 parent 指向的目录下创建文件节点,并启动该类型内核对象的hotplug 函数。int kobject_register(str
11、uct kobject * kobj);kobject 注册函数。通过调用 kobject_init()初始化 kobj,再调用 kobject_add()完成该内核对象的注册。void kobject_del(struct kobject * kobj);从 Linux 设备层次(hierarchy)中删除 kobj 对象。void kobject_unregister(struct kobject * kobj);kobject 注销函数。与 kobject_register()相反,它首先调用 kobject_del 从设备层次中删除该对象,再调用 kobject_put()减少该对象的
12、引用计数,如果引用计数降为 0,则释放该kobject 对象。3.2 kset 相关函数与 kobject 相似,kset_init() 完成指定 kset 的初始化,kset_get()和 kset_put()分别增加和减少kset 对象的引用计数。 Kset_add()和 kset_del()函数分别实现将指定 keset 对象加入设备层次和从其中删除;kset_register()函数完成 kset 的注册而 kset_unregister()函数则完成 kset 的注销。3.3 subsystem 相关函数subsystem 有一组完成类似的函数,分别是:void subsystem_
13、init(struct subsystem *subsys);int subsystem_register(struct subsystem *subsys);void subsystem_unregister(struct subsystem *subsys);struct subsystem *subsys_get(struct subsystem *subsys)void subsys_put(struct subsystem *subsys); 设备模型组件在上述内核对象机制的基础上,Linux 的设备模型建立在几个关键组件的基础上,下面我们详细阐述这些组件。4.1 devices系统
14、中的任一设备在设备模型中都由一个 device 对象描述,其对应的数据结构 struct device定义为:struct device struct list_head g_list; struct list_head node;struct list_head bus_list;struct list_head driver_list;struct list_head children;struct device *parent;struct kobject kobj;char bus_idBUS_ID_SIZE;struct bus_type *bus;struct device_dri
15、ver *driver;void *driver_data;/* Several fields omitted */;g_list 将该 device 对象挂接到全局设备链表中,所有的 device 对象都包含在 devices_subsys中,并组织成层次结构。Node 域将该对象挂接到其兄弟对象的链表中,而 bus_list 则用于将连接到相同总线上的设备组织成链表,driver_list 则将同一驱动程序管理的所有设备组织为链表。此外,children 域指向该 device 对象子对象链表头,parent 域则指向父对象。Device 对象还内嵌一个 kobject 对象,用于引用计数
16、管理并通过它实现设备层次结构。Driver 域指向管理该设备的驱动程序对象,而 driver_data 则是提供给驱动程序的数据。Bus域描述设备所连接的总线类型。内核提供了相应的函数用于操作 device 对象。其中 Device_register()函数将一个新的 device对象插入设备模型,并自动在/sys/devices 下创建一个对应的目录。Device_unregister()完成相反的操作,注销设备对象。 Get_device()和 put_device()分别增加与减少设备对象的引用计数。通常 device 结构不单独使用,而是包含在更大的结构中作为一个子结构使用,比如描述
17、PCI 设备的 struct pci_dev,其中的 dev 域就是一个 device 对象。4.2 drivers系统中的每个驱动程序由一个 device_driver 对象描述,对应的数据结构定义为:struct device_driver char *name; 设备驱动程序的名称struct bus_type *bus; 该驱动所管理的设备挂接的总线类型struct kobject kobj; 内嵌 kobject 对象struct list_head devices; 该驱动所管理的设备链表头int (*probe)(struct device *dev); 指向设备探测函数,用于探
18、测设备是否可以被该驱动程序管理int (*remove)(struct device *dev); 用于删除设备的函数/* some fields omitted*/;与 device 结构类似, device_driver 对象依靠内嵌的 kobject 对象实现引用计数管理和层次结构组织。内核提供类似的函数用于操作 device_driver 对象,如 get_driver()增加引用计数,driver_register()用于向设备模型插入新的 driver 对象,同时在 sysfs 文件系统中创建对应的目录。Device_driver()结构还包括几个函数,用于处理热拔插、即插即用和电
19、源管理事件。4.3 buses系统中总线由 struct bus_type 描述,定义为:struct bus_type char * name; 总线类型的名称struct subsystem subsys; 与该总线相关的 subsystemstruct kset drivers; 所有与该总线相关的驱动程序集合struct kset devices; 所有挂接在该总线上的设备集合struct bus_attribute * bus_attrs; 总线属性struct device_attribute * dev_attrs; 设备属性struct driver_attribute * d
20、rv_attrs; 驱动程序属性int (*match)(struct device * dev, struct device_driver * drv);int (*hotplug) (struct device *dev, char *envp, int num_envp, char *buffer, int buffer_size); int (*suspend)(struct device * dev, u32 state);int (*resume)(struct device * dev);;每个 bus_type 对象都内嵌一个 subsystem 对象,bus_subsys 对
21、象管理系统中所有总线类型的 subsystem 对象。每个 bus_type 对象都对应/sys/bus 目录下的一个子目录,如 PCI 总线类型对应于/sys/bus/pci。在每个这样的目录下都存在两个子目录:devices 和 drivers(分别对应于 bus_type 结构中的 devices 和 drivers 域) 。其中 devices 子目录描述连接在该总线上的所有设备,而 drivers 目录则描述与该总线关联的所有驱动程序。与 device_driver 对象类似,bus_type 结构还包含几个函数(match()、hotplug()等)处理相应的热插拔、即插即拔和电源
22、管理事件。4.4 classes系统中的设备类由 struct class 描述,表示某一类设备。所有的 class 对象都属于class_subsys 子系统,对应于 sysfs 文件系统中的/sys/class 目录。每个 class 对象包括一个class_device 链表,每个 class_device 对象表示一个逻辑设备,并通过 struct class_device 中的 dev 域(一个指向 struct device 的指针)关联一个物理设备。这样,一个逻辑设备总是对应于一个物理设备,但是一个物理设备却可能对应于多个逻辑设备。此外,class 结构中还包括用于处理热插拔、即插即拔和电源管理事件的函数,这与 device 对象和 driver 对象相似。