1 Kobject
1.1 简介
Kobject 是Linux 2.6 引入的新的设备管理机制,在内核中由struct kobject数据结构进行描述通过这个数据结构使所有设备在底层都具有统一的接口,kobject提供基本的对象管 理,是构成Linux2.6设备模型的核心结构,它与sysfs文件系统紧密关联,每个在内核中注 册的kobject对象都对应于sysfs文件系统中的一个目录。Kobject是组成设备模型的基本结 构。但是,它比较低调,从不单独使用,都是嵌套在别的数据结构中。类似于C++中的基 类,它嵌入于更大的对象的对象中–所谓的容器–用来描述设备模型的组件。如bus, devices, drivers 都是典型的容器。这些容器就是通过kobject连接起来了,形成了 一个树状结构。这个树状结构就与/sys相对应。
kobject 结构为一些大的数据结构和子系统提供了基本的对象管理,避免了类似机能的重 复实现。这些机能包括
- 对象引用计数.
- 维护对象集合.
- 对象上锁.
- 在用户空间的表示.
1.2 定义
Kobject结构定义为:
struct kobject { const char *name; struct list_head entry; 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; unsigned int uevent_suppress:1; };
其中,重要的变量已经加了注释,这里再简要介绍一下:
- kref:
kref域表示该对象引用的计数,内核通过kref实现对象引用计数管理,内核提供两 个函数kobject_get()、kobject_put() 分别用于增加和减少引用计数,当引用计数为 0时,所有该对象使用的资源释放。 - Ktype:
域是一个指向kobj type结构的指针,表示该对象的类型。 - parent
指针指向kobject的父对象。因此,kobject就会在内核中构造一个对象层次结构, 并且可以将对各对象间的关系表现出来,就如你看到的,这便是sysfs的真正面目: 一个用户空间的文件系统,用来表示内核中kobject对象的层次结构。
1.3 相关函数
-
- void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
-
- kobject初始化函数,用于初始化kobject结构,在一些例行的检查之后,该 函数通过kobject_init_internal()来初始化kobjet的内部变量:初始化 kobjet的易用计数,初始化双向链表entry,设置kobject的四个state变量。 随后,将这个kobj的类型设置成了入参的ktype。
- int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, va_list vargs)
-
- 设置指定kobject的名称。
- static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs)
-
- 调用kobject_set_name_vargs来设置kobj的名字,设置kobj的parent,并调 用kobject_add_internal将该kobj加入到kset中。
- static int kobject_add_internal(struct kobject *kobj)
-
- 检查并更新kobj的Parent。如果kobj没有parent,但是其kset存在,则将该 kobj加入到kset的List中,然后将Kset的内嵌的kobjet 设置为这个kobj的 parent。随后,调用create_dir在sysfs中创建节点, 并更新kobject的状 态。
- int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...)
-
- 初始化并将kobjt添加到指定的kset里面去。内部调用了前面提到的 kobject_init_internal, kobject_add_internal等等。
- struct kobject *kobject_get(struct kobject *kobj)
-
- 将kobj 对象的引用计数加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(struct 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()减少该对象的引用计数,如果引用计数降为0, 则释放kobject对象。
2 Kobj type
kobject对象被关联到一种特殊的类型,即ktype。 ktype由kobj_type结构体表示,定义于 <linux/kobject.h>中:
struct kobj_type { void (*release)(struct kobject *kobj); struct sysfs_ops *sysfs_ops; struct attribute **default_attrs; };
- release指针
- 指向在kobject引用计数减至0时要被调用的析构函数。该函 数负责释放 所有kobject使用的内存和其它相关清理工作。
- ktype
- 是为了描述一族kobject所具有的普遍特性。因此,不再需要 每个kobject都分别定 义自己的特性,而是将这些普遍的特性在ktype结构体中一次定义,然后所有“同类”的 kobject都能共享一样的特性。
- default_attrs
指向一个attribute结构体数组。这些结构体定义了该 kobject相关的默认 属性。属性给定了对象的特征,如果该kobject被导出到sysfs中,那么这些属性都将相应 的作为文件而导出。其定义如下:
struct attribute { const char *name; struct module *owner; mode_t mode; };
其中name字段提供了该属性的名称,最终出现在sysfs中的 文件名就是它。owner字段在存 在所属模块的情况下指向其所属的module结构体。如果一个模块没有该属性,那么该字段 为NULL。mode字段类型为modet,他表示sysfs中该文件的权限。
该数据结构以文件的形式输出到sysfs的目录当中。在 kobject对应的目录下面。文件 名就是name。文件读写的方法对应于kobj_type中的sysfs_ops。
- sysfs_ops
指向sysfs操作表和一个sysfs文件系统缺省属性列表,定 义如下:
struct sysfs_ops { ssize_t (*show)(struct kobject *, struct attribute *, char *); ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t); };
Sysfs操作表包括两个函数store()和show()。
- show
当用户态读取属性时,show()函数被调 用,show()方法在读操作时被调用。它会拷贝由 attr提供的属性值到buffer指定的缓冲区,缓冲区大小为PAGESIZE字节;该 函数若执 行成功,返回写入buffer的字节数,失败,返回负的错误码。
- store store()方法在写操作时调用,它会从buffer中读取size大小的字节,并将其放入 attr表示的属性结构体变量中。
3 kset
3.1 简介
kset是kobject对象的集合体。把它看 成一个容器,可将所有相关的kobject对象,比 如“全部的块设备”置于同一位置。其最重要的作用是建立上层(sub-system)和下层的(kobject)的关联性。
kobject 也会利用它了分辨自已是属于那一個类型,然後在/sys 下建立正确的目录位置。而kset 的优先权 比较高,kobject会利用自已的*kset 找到自已所属的kset,並把*ktype 指定成該kset下 的ktype,除非沒有定义kset,才会用ktype來建立关系。
Kobject通过kset组织成层次化的结 构,kset是具有相同类型的kobject的集合,在内核中 用kset数据结构表示,定义为:
struct kset { struct subsystem * subsys; 所在的subsystem的指针 struct kobj type * ktype; 指向该kset对象类型描述符的指针 struct list head list; 用于连接该kset中所有kobject的链表头 struct kobject kobj; 嵌入的kobject,用于计数,并被因为父类 struct kset hotplug ops * hotplug ops; 指向热插拔操作表的指针 };
其中:
- 包含在kset中的所有kobject被组织成一个双向循环链 表,list域正是该链表的头。
- Ktype域指向一个kobj type结构,被该kset中的所有kobject共享,表示这些对象的类 型。
- Kset数据结构还内嵌了一个kobject对象(由kobj域表 示),所有属于这个kset 的
kobject对象的parent域均 指向这个内嵌的对象。
- kset还依赖于kobj维护引用计数:kset的引用计数 实际上就是内嵌的 kobject对象的引用计数。
3.2 相关函数
- ksetinit()
- 完成指定kset的初始化
- ksetget() 和ksetput()
- 分别增加和减少kset对象的 引用计数。
- Ksetadd() 和ksetdel()
函数分别实现将指定keset 对象加入设备层次和从其中删除;
- ksetregister() 和ksetunregister()
- 完成 kset的注册和注销。
4 subsystem
4.1 简介
如果说kset 是管理kobject 的集合,那么,subsystem 就是管理kset 的集合。 它描述系统中某一类设备子系统,如block subsys表示所有的块设备,对应于sysfs文件系统中的 block目录。类似的,devices subsys对应于sysfs中的devices目录,描述系统中所有的 设备。Subsystem由struct subsystem数据结构描述,定义为:
struct subsystem { struct kset kset; 内嵌的kset对象 struct rw semaphore rwsem; 互斥访问信号量 };
可以看出,subsystem 与kset的区别就是多了一个信号量,所以在后来的代码中,subsystem已经完全被kset取缔了。
每个kset属于某个 subsystem,通过设置kset结构中的subsys域指向指定的subsystem可以 将一个kset加入到该subsystem。所有挂接到同一subsystem的kset共享同一个rwsem信号 量,用于同步访问kset中的链表。
4.2 相关函数
subsystem有一组类似 的函数,分别是:
void subsysteminit(struct subsystem *subsys);
int subsystemregister(struct subsystem *subsys);
void subsystemunregister(struct subsystem *subsys);
struct subsystem *subsysget(struct subsystem *subsys)
void subsysput(struct subsystem *subsys);
5 sysfs
sysfs文件系统是一个处于 内存中的虚拟文件系统,它为我们提供了kobject对象层次结 构的视图。帮助用户可以一个简单文件系统的方式来观察系统中各种设备的拓扑结 构。借助属性对象,kobject可用导出文件的方式,将内核变量提供给用户读取或 写入。
sysfs的诀窍是把 kobject对象与目录项紧密联系起来,这点是通过kobject中的 dentry(directory entry)字段实现的。前面提到,dentry结构体表示目录 项,通过连接kobject到指定的目录项上,无疑方便的将kobject映射到该目录上。 好了,kobject其实已经形成了一棵树了——就是我们心爱的对象模型体系。
sysfs的根目录下包含了七 个子目录:block、bus、class、devices、firmware、 module和power。block目录下的每个子目录都对应着系统中的一个块设备。反过 来,每个目录下又都包含了该块设备的所有分区。bus目录提供了一个系统总线视 图。class目录包含了以高层功能逻辑组织起来的系统设备视图。devices目录是 系统中设备拓扑结构视图,它直接映射出了内核中设备结构体的组织层次。 firmware目录包含一些诸如ACPI、EDD、EFI等低层子系统的特殊树。power目录包 含了系统范围的电源管理数据。
5.1 sysfs中添加和删除kobject
仅仅初始化一个kobject 是不能自动将其导出到sysfs中的,想要把kobject导入 sysfs,需要用到函数kobjectadd():
int kobject_add(struct kobject *kobj);
kobject在sysfs中 的位置取决于kobject在对象层次结构中的位置。如果kobject 的父指针被设置,那么在sysfs中kobject 将被映射为其父目录下的子目录,如果 parent没有设置,那么kobject将被映射到kset->kobj中的子目录。两者都未设 置,映射为 sysfs下的根级目录。
5.2 向sysfs中添加文件
我们已经看到kobject被 映射为文件目录,而且所有的对象层次结构都优雅的、一 个不少的映射成为sys下的目录结构。但是里面的文件是什么?sysfs仅仅是一个 漂亮的树,但是没有提供实际数据的文件。