- Tekkaman:http://blog.chinaunix.net/space.php?uid=20543672&do=blog&id=94312
- 《Linux那些事儿 之 我是Sysfs》:
- ://hi.baidu.com/csdeny/blog/item/5a4f14f342d30dca0a46e038.html
- 还有国嵌的实验代码,对我理解kobject帮助很大,一并表示感谢
设备模型这一块相对来说比较复杂,不管是LDD3还是宋宝华的书,讲的都很抽象,我以前也看了好几遍,都是云里雾里,没有理出个头绪(是偶智商低么?
)。ldd3认为设备模型这一块属于高级教材,对于多数程序来说是不必要的,但我实际工作中发现,虽然设备模型内核已经处理的很好,几乎不需要改动,但正确理解其工作机理,对认知Linux驱动,几乎可以说是个质的提升。so,还是静下心来,吧这个部分好好梳理一下,采用的方法是自下而上,先弄清楚最基本的概念,(以前方法相反,效果很杯催...)。
Linux 2.6内核的一个重要特色是提供了统一的内核设备模型。随着技术的不断进步,系统的拓扑结构越来越复杂,对智能电源管理、热插拔以及plug and play的支持要求也越来越高,2.4内核已经难以满足这些需求。为适应这种形势的需要,2.6内核开发了全新的设备模型。
1.sysfs
"sysfs is a ram-based filesystem initially based on ramfs. It provides a means
to export kernel data structures, their attributes, and the linkages between them to
userspace.”
--- documentation/filesystems/sysfs.txt
Sysfs文件系统是一个类似于proc文件系统的特殊文件系统,用于把连接到系统上的设备和总线组织成分级的文件结构,向用户模式程序提供详细的内核数据结构信息。
- # ls /sys
- class firmware vendor
- bus devices kernel module
- #
block: 包含所有的快设备,系统中的每个块设备在该目录下对于一个子目录,每个子目录包含一些属性文件,loop块设备使用文件来模拟的。
devices: 包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构。
bus: 在内核中注册的每条总线在该目录下对应一个子目录,每个总线又包含两个子目录devices,drivers,devices目录包含整个系统中属于该总线类型的设备,drivers目录包含所有属于该总线的驱动
drivers: 包含内核中已经注册的所有设备驱动
class: 系统中的设备类型,按功能划分。
kernel:内核中的配置参数
module:系统中的所有模块信息
sysfs这样被挂载的。
mount -t sysfs sysfs /sys
sysfs是一个特殊文件系统,并没有一个实际存放文件的介质。断电就消失了。简而言之,sysfs的信息来源是kobject层次结构,读一个sysfs文件,就是动态的从kobject结构提取信息,生成文件。
所以,首先要研究下sysfs文件系统的信息来源 -- kobject层次结构。kobject层次结构就是linux的设备模型。
2.kobject
kobject实现了基本的面向对象管理机制,是构成Linux2.6设备模型的核心结构,它与sysfs文件系统紧密相连,在内核中注册的每个kobject对象对应sysfs文件系统中的一个目录。kobject可以理解为类似JAVA,C++中基类,常被嵌入到其他类型(容器)中,比如bus,devices,drivers都是典型的容器,他们继承kobject基本功能,并扩展自身其他功能,这些容器通过通过kobject链接起来,形成树状结构。C语言中没有继承特性,通过结构体包含来实现类继承。
①kobject定义在 。
- struct kobject {
- const char * k_name;/*kobject 的名字数组(sysfs 入口使用的名字)指针;如果名字数组大小小于KOBJ_NAME_LEN,它指向本数组的name,否则指向另外分配的一个名字数组空间 */
- [KOBJ_NAME_LEN];/*kobject 的名字数组,若名字数组大小不小于KOBJ_NAME_LEN,只储存前KOBJ_NAME_LEN个字符*/
- ;/*kobject 的引用计数*/
- ;/*kobject 之间的双向链表,与所属的kset形成环形链表*/
- * parent;/*在sysfs分层结构中定位对象,指向上一级kset中的struct kobject kobj*/
- * kset;/*指向所属的kset*/
- * ktype;/*负责对该kobject类型进行跟踪的struct kobj_type的指针*/
- * dentry;/*sysfs文件系统中与该对象对应的文件节点路径指针*/
- ;/*等待队列头*/
- };
kobject 是组成设备模型的基本结构,初始它只被作为一个简单的引用计数, 但随时间的推移,其任务越来越多。现在kobject 所处理的任务和支持代码包括:
对象的引用计数 :跟踪对象生命周期的一种方法是使用引用计数。当没有内核代码持有该对象的引用时, 该对象将结束自己的有效生命期并可被删除。
sysfs 表述:在 sysfs 中出现的每个对象都对应一个 kobject, 它和内核交互来创建它的可见表述。
数据结构关联:整体来看, 设备模型是一个极端复杂的数据结构,通过其间的大量链接而构成一个多层次的体系结构。kobject 实现了该结构并将其聚合在一起。
热插拔事件处理
一个kobject对自身并不感兴趣,它存在的意义在于把高级对象连接到设备模型上。因此内核代码很少(甚至不知道)创建一个单独的 kobject;而kobject 被用来控制对大型域(domain)相关对象的访问,所以kobject 被嵌入到其他结构中。kobject 可被看作一个最顶层的基类,其他类都它的派生产物。 kobject 实现了一系列方法,对自身并没有特殊作用,而对其他对象却非常有效。
对于给定的kobject指针,可使用container_of宏得到包含它的结构体的指针。
②kobject方法
- void kobject_init(struct kobject *kobj, struct kobj_type *ktype)/*初始化kobject结构*/
- int kobject_add(struct kobject *kobj, struct kobject *parent,const char *fmt, ...)//将kobject对象注册到Linux系统
- int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,struct kobject *parent, const char *fmt, ...)
- /*初始化kobject,并将其注册到Linux系统,手头实验平台是2.6.10的代码,注册用kobject_register(),基本过程是一样的*/
- int kobject_set_name(struct kobject *kobj, const char *fmt, ...)//设置kobject目录名字
- (struct kobject *kobj)/*从Linux系统中删除kobj对象*/
- *kobject_get(struct kobject *kobj)
- //将kobject对象的引用计数加1,同时返回该对象指针
- (struct kobject *kobj)
- //将kobject对象的引用计数减1,如果引用计数为0,则调用release方法释放该kobject对象。
③kobj_type
kobject的ktype成员是一个指向 kobj_type结构的指针,该结构记录了kobject的一些属性
- struct kobj_type {
- (*release)(struct kobject *kobj);
- *sysfs_ops;
- **default_attrs;
- };
release:用于释放kobject占用的资源,当kobject的应用计数为0时被调用。
- struct attribute {
- const char *name;
- struct module *owner;
- mode_t mode;
- };
struct attribute:对应于kobject下的一个文件,name就是文件名。
- struct sysfs_ops {
- (*show)(struct kobject *, struct attribute *,char *buf);
- (*store)(struct kobject *,struct attribute *,const char *buf, size_t);
- };
show: 当用户读属性文件时,被调用,该函数将属性值写入buf返回给用户态。
store:当用户写属性文件时,被调用,用户存储用户传入的属性值。
加载一个kobject过程:
① 分配kobject内存空间,并清零(必须清零,否则出错,我板子上出现段错误)
② 设置kobject目录名字,ktype等成员
③ 初始化,加载kobject
测试代码: kobject.rar
实验现象:
kobject可以简单通俗的理解为一个对象,功能是用来在sysfs文件系统中管理一个目录,以及目录下文件属性。Linux内核模型中的几个典型模块bus,device,driver都是通过继承kobject对象(结构体包含),来组织sysfs文件系统的层级目录树状图。kobject是设备模型中最核心的数据结构,真正理解这个对象机制,才能掌握好后面的总线模型。
3.kset
kset是具有相同类型的object的集合,在sysfs中体现为一个目录,
- struct kset {
- * ktype; /*指向该kset对象类型的指针*/
- ;/*用于连接该kset中所有kobject以形成环形链表的链表头*/
- ;/*用于避免竞态的自旋锁*/
- ; /*嵌入的kobject*/
- * uevent_ops;
- /*处理热插拔时间的集合*/
- };
kset方法,与kobject类似
- void kset_init(struct kset *kset);
- int kset_add(struct kset *kset);
- int kset_register(struct kset *kset);
- (struct kset *kset);
- /*管理 ksets 的引用计数:*/
- *kset_get(struct kset *kset);
- (struct kset *kset);
- /* kset 也有一个名字,存储于嵌入的 kobject,因此设置它的名字用:*/
- (&my_set->kobj, "The name");
热插拔事件
一个热插拔事件是一个从内核空间发送到用户空间的通知, 表明系统配置已经改变. 无论 kobject 被创建或删除,都会产生这种事件。热插拔事件会导致对 /sbin/hotplug 的调用, 它通过加载驱动程序, 创建设备节点, 挂载分区或其他正确动作响应事件。
热插拔事件的实际控制是通过一套存储于 kset_uevent_ops (《LDD3》中介绍的struct kset_hotplug_ops * hotplug_ops;在2.6.22.2中已经被kset_uevent_ops 结构体替换)结构的方法完成:
struct kset_uevent_ops { |
可以在 kset 结构的uevent_ops 成员中找到指向kset_uevent_ops结构的指针。
若在 kobject 中不包含指定的 kset , 内核将通过 parent 指针在分层结构中进行搜索,直到发现一个包含有kset的 kobject ; 接着使用这个 kset 的热插拔操作。当kset管理的kobject和kset发生变化时,这三个方法被调用。
三个方法功能如下:
(1) filter 函数让 kset 代码决定是否将事件传递给用户空间。如果 filter 返回 0,将不产生事件。以磁盘的 filter 函数为例,它只允许kobject产生磁盘和分区的事件,源码如下:
static int block_hotplug_filter(struct kset *kset, struct kobject *kobj) |
(2) 当调用用户空间的热插拔程序时,相关子系统的名字将作为唯一的参数传递给它。name 函数负责返回合适的字符串传递给用户空间的热插拔程序。
(3)热插拔脚本想得到的任何其他参数都通过环境变量传递。uevent 函数的作用是在调用热插拔脚本之前将参数添加到环境变量中。函数原型:
int (*uevent)(struct kset *kset, struct kobject *kobj, /*产生事件的目标对象*/ |
热插拔事件的产生通常是由在总线驱动程序层的逻辑所控制。
4.子系统
子系统是对整个内核中一些高级部分的表述。子系统通常(但不一定)出现在 sysfs分层结构中的顶层,内核子系统包括 block_subsys(/sys/block 块设备)、 devices_subsys(/sys/devices 核心设备层)以及内核已知的用于各种总线的特定子系统。
每个 kset 必须属于一个子系统,子系统成员帮助内核在分层结构中定位 kset 。由于子系统在新版内核中已经被去掉用kset代替了。,就不详细研究了。