kobject分析
根据文章The zen of kobjects所述,kobject设计之初是为了管理内核对象的引用计数,但在内核的发展过程中,添加了更多的功能;对于较新版本的内核,我理解的kobject的功能主要是:
- 管理内核对象引用计数
- 为内核中的对象创建层级
- 在sysfs中为对象创建文件夹
每一个kobject在文件系统/sys路径下都有对应的文件夹;既然可以以目录的形式表示kobject,那么kobject一定是按照树形结构来划分层级的。
kobject结构
先看部分精简后的kobject的数据结构
1 | struct kobject { |
其中kset和ktype是比较重要的两个内容:
- kset是一个kobject组,其本身也是一个kobject
- ktype表示当前kobject的类型,实际上作用是表示当对象引用计数为0时,如何释放对象
初始化kobject
kobject的初始化函数是kobject_init(),而kobject_init()中又调用了kobject_init_internal()函数,两个函数的实现都非常简单,精简掉错误处理部分后代码如下:
1 | static void kobject_init_internal(struct kobject *kobj) |
实际上初始化操作就只为kobject结构中ktype及各个字段进行赋值,并且对kref字段使用kref_init()初始化引用计数,继续跟一下kref的结构和kref_init()的实现。
1 | struct kref { |
kref_init()将引用初始化为1,所以kobject初始化之后,其引用计数为1。
需要注意kobject_init()函数并未对kobject指针分配内存,所以调用kobject_init()之前,需要手动为其分配内存;但是也有另一个函数,包含了分配内存和初始化的操作:
1 | struct kobject *kobject_create(void) |
kobject_create()函数将kobject的ktype设置为了dynamic_kobject_ktype,这是一个全局的kobj_type,声明如下:
1 | static struct kobj_type dynamic_kobj_ktype = { |
前面提到ktype的作用就是指示当kobject的引用归零时,如何处理kobject;而这里的kobj_type->release就是对应的处理函数,这个dynamic_kobj_release实现如下:
1 | static void dynamic_kobj_release(struct kobject *kobj) |
由于是使用kobject_create()默认设置的ktype,所以release时也只进行了kfree操作。
增减引用计数
kobject对象的引用存储在其kref字段中,使用kobject_get()和kobject_put()函数可以增加和减少kobject引用计数。
增加引用
kobject_get()实现比较简单:
1 | struct kobject *kobject_get(struct kobject *kobj) |
检查kobject是否为空,然后调用了kref_get():
1 | static inline void kref_get(struct kref *kref) |
根据函数名可以确认这就是将kref->refcount记录的引用计数加1。
减少引用
减少引用除了要减少kref->refcount,还需要每次检查其值是否为0,如果引用被减少到0,则应该调ktype->release
1 | void kobject_put(struct kobject *kobj) |
kobject_put()在调用kref_put()的时候,将kobject_release()传递给了kref_put(),当引用减少为0时,会调用此函数;改函数精简后源码如下:
1 | static void kobject_release(struct kref *kref) |
这里通过kref获取到对应的kobject,然后调用kobject_cleanup():
1 | static void kobject_cleanup(struct kobject *kobj) |
在kobject_cleanup()中,调用了ktype->release,对资源进行了释放。
kset结构
kset本身也是一个kobject,其结构比较简单:
1 | struct kset { |
前面说到kset表示一个kobject组,从结构来看,kset也包含kobject结构,所以kset在sysfs中也有对应的文件夹;除了kset_uevent_ops以外,kset结构本身没有其他可以存储数据的部分,所以我认为kset的作用就是为其组内的kobject提供统一的uevent事件。
kset组织kobject
kobject结构本身包含一个指向kset的指针,但是kset却没有指向子kobject的指针,所以如果需要通过kset找到组内的kobject,只能通过kset->list遍历。
先来看一下kobject_add_internal()的部分:
1 | static int kobject_add_internal(struct kobject *kobj) |
该函数由kobject_add()调用,除了设置kobject的parent之外,还调用了kobject_kset_join():
1 | /* add the kobject to its kset's list */ |
该函数将kobject添加到了kset指针域的尾部,这样就可以通过kset找到组内的所有kobject,kset与组内的kobject组成了下图所示的结构:

这里需要注意kobject_init()和kobject_add()都没有设置kobject->kset的值,需要手动设置kobject所属的kset。