一、kobject
kobject是linux内核做的一个抽象,它本身不具备实际的含义。它被嵌入到各种数据结构中,因而只要我们具有kobject就可以获取并访问它所嵌入的宿主数据结构。这样就提供了良好的组织管理能力。宿主数据结构可以千变万化,但是其嵌入的kobject很简单,可以基于该结构做很多通用的事情。sysfs就是利用这一点实现的。该文件系统以很简单的方式实现了对内核中各种复杂部件(尤其是硬件相关部分)的统一管理。这非常类似于面向对象的思想,kobject相当于一个基类,其它类是它的派生类。。。根据kobject所采用的思想,在使用该结构时,我们经常需要做的就是获取其宿主数据结构,这可以通过container_of宏来完成。
内嵌kobject的一个例子就是设备的基础数据结构struct device,它内部就包含了一个kobject数据结构。
对于sysfs来说,其中的每一个目录必定对应一个kobject对象。每个kobject的属性会成为sysfs中的一个文件。
1.1 数据结构
kobject被设计为可以反映出数据结构的层次关系,其定义如下所示:- name:该kobject的名字,会作为文件名出现在对应的sysfs中
- entry:链表元素,用于将该kobject链接到它所属的kset的链表中。
- parent:指向该对象的父对象
- kset:如果该对象属于一个kset,则指向所属的kset
- ktype:包含了类型信息,包括该对象的操作函数指针以及销毁该对象时的释放函数
- sd:该对象对应的sysfs节点数据结构
- kref:引用计数
kobj_type的定义如下:
- ktype用来表示一类kobject的公共内容,主要包括了对该kobject进行操作相关的函数指针信息。每个kobject都要有一个对应的kobj_type结构,在kobject_init或者kobject_init_and_add调用时给出。
- release:函数指针,该类kobject的销毁函数。
- sysfs_ops:包含了如何实现kobject的属性信息的函数指针
- default_attrs:默认属性链表,这些属性会在这类kobject创建时自动被创建(populate_dir中)。
struct sysfs_ops的定义如下所示:
1.2. kobject 初始化
kobject的初始化比较复杂,它至少包含如下几个操作- 将整个kobject设置为0
- 调用kobject_init来初始化该kobject,这时需要提供一个kobj_type,它包含了操作该kobject的信息以及销毁该kobject的函数
1.3 kobject对象操作
1.3.1 将对象添加到sysfs
kobject_add用于将一个kobject添加到sysfs中,它需要调用者提供该对象的父对象以及该对象的名字作为参数。同时如果kobject需要和一个特定的kset关联,在调用kobject_add之前要先设置该对象的kset。kobject_add会将kobject添加到kset中。如果一个kobject与一个kset关联,那它的父节点可以设为NULL,这样kobject的父节点会在调用kobject_add时自动设为kset内部的kobject。1.3.2 kobject对象改名
如果需要修改kobject的名字,需要调用kobject_rename来完成,而不是直接修改。如果要获取kobject的名字可以使用函数kobject_name来获取。1.3.3 通知userspace
在将kobject添加到sysfs后,可以通过kobject_uevent向userspace发送通告通知,此时action参数为KOBJ_ADD。如果想通知userspace删除一个对象,可以通过kobject_uevent向userspace发送action参数为KOBJ_REMOVE的消息。
需要注意的是,如果一个kobject以及它的祖先都不属于某个kset,则内核就不会允许它发送通告,因而如果要支持uevent,则内核部件必须属于某个kset。
1.1.4 引用计数的操作
kobject的一个关键作用是它可以作为它所嵌入的宿主数据结构的引用计数。因为它包含了一个kref数据结构。kobject的引用计数操作API为kobject_get何kobject_put。kobject_get调用成功时会增加kobject中的kref的计数值,同时会返回指向kobject的指针。
kobject_put的调用会递减kobject中的kref的引用计数值,,同时如果引用计数变为0,则会调用该kobject的ktype的release函数释放该kobject对象。在删除时,kobject还会检查该对象是否向userspace发送过KOBJ_ADD消息,如果发送过该消息并且到此时还没发送过KOBJ_REMOVE消息,则就会发送一个KOBJ_REMOVE消息到userspace。这样做是因为如果发送过KOBJ_ADD消息,则userspace的工具就会处理该消息,在这个过程中应该有打开相应sysfs文件的动作,这就会导致应用计数增加,如果不发送相应的KOBJ_REMOVE消息,则相应的文件的引用计数就无法减小,从而导致其它的问题。
从这里也可以看出,kobject的释放是异步的,这是正常的,因为无法预期kobject的使用情况,也就无法确定何时释放它,因而内核采用了一贯的做法,使用一个引用计数,当引用计数为0时就释放。
1.1.5 创建简单的kobject
有时开发者只想在sysfs中创建一个简单的目录,而且不想考虑与kset、show函数、store函数等一系列细节问题。这时就可以使用kobject_create_and_add来创建一个简单的kobject。这个函数会创建一个kobject,并把它放在sysfs中parent的子目录中。为了创建这个kobject的相关属性,可以使用函数sysfs_create_file和sysfs_create_group。
1.4 kobject非默认属性
默认情况下kobject的default_attrs成员描述了kobject拥有的所有属性。但是kobject还提供了另外的机制用来添加新的属性。如果想要为一个kobject添加默认属性外的属性,可以调用以下API:
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
调用完后,系统会使用属性中的名字为该kobject的该属性创建一个属性文件。不过需要注意的是这里没有提供新的kobject_type,也即没有新的属性操作函数,因此在添加新的属性之前必须确保kobject的属性操作函数知道如何处理新的属性。
可以使用以下API将属性删除:
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
调用完该函数后,属性文件将从sysfs中消失,但是用户可能还打开有该属性文件,因而还有可能对其进行读写。
二、kset
kset是一些kobject的集合,这些kobject通过kset关联在一起。一个kset中的kobject可以使用不同的ktype。一个kset有如下功能:
- 它作为一个容器,包含一组对象。它可以被内核用来跟踪所有的块设备或者所有的PCI设备驱动。
- 一个kset在sysfs中变现为一个分层的目录。每个kset都有一个内部kobject,作为其它kobject的父目录,其余各个kobject都是它的子目录。sysfs的顶层目录结构也是这样组成的。
- kset可以支持kobject的热插拔,并且影响uevent事件如何被发布到用户空间。
kset用一个标准的链表来管理它的子kobject。kset的子kobject的kset域指向了该kset,kset的子kobject的parent会被设置为kset的内部kobject。不同于kobject的是ksets肯定会在sysfs 中出现,一旦创建了一个
kset并且将其添加到了系统中,则就会有一个相应的sysfs生成。kobject可能并不出现在sysfs中,但是kset的每个kobject成员都会出现在sysfs中。
2.1 kset的数据结构
kset的数据结构如下所示:- list:子kobject链表
- list_lock:保护链表的自旋锁
- kobj:kset内嵌的kobject,kset的子kobject的parent会指向它。
- uevent_ops:该kset的uevent操作函数集
2.2 kset的初始化
kset的初始化类似于kobject的初始化,包括(可以参考函数__bus_register或者__class_register):- 分配数据结构
- 进行初始化,初始化kset内嵌的kobject
- 调用kset_register,它会调用kobject_init_internal和kobject_add_internal初始化kset的kobject,并且调用kobject_uevent发出KOBJ_ADD事件
当你不再使用一个kset时,可以用kset_unregister()释放它。
2.3 kset的其它操作
2.3.1 引用计数
类似于kobject,可以通过函数kset_get和kset_put来管理kset的引用计数。2.3.2 kset名字
kset的名字保存在其内嵌的kobject中。因此设置其名字就是调用kobject的名字设置函数。
三、热插拔
热插拔事件时内核发送到用户空间的通知,用于表明系统配置出现了变化。在kobject被创建和删除时都会产生该消息,热插拔消息一般由用户空间的一个进程处理(最新的都是uevent守护进程)。用户空间进程会根据收到的信息来做比如加载驱动、挂载分区等等事情。
该函数会根据分层结构逐层向上查找第一个kset,找到一个kset后,就会利用该kset中的kset_uevent_ops来进行消息的过滤、环境变量准备,然后才把准备好的消息发送给用户空间。
3.1 数据结构
支持热插拔的数据结构是:- filter:该函数用来阻止kset中某个特定的kobject向用户控件发送uevent。如果函数结果返回0,则不会发送。
- name:该函数是用来覆盖uevent发送到用户控件的kset名字。默认情况下,这个名字和kset的名字一样,但如果提供了name函数,这个名字会被覆盖。
- uevent:该函数在uevent将被发送到用户空间时调用,可以在uevent中添加更多的环境变量。
3.2 热插拔消息的产生
在kobject产生热插拔消息后,该消息会被kobject_uevent_env函数处理。该函数会根据分层结构逐层向上查找第一个kset,找到一个kset后,就会利用该kset中的kset_uevent_ops来进行消息的过滤、环境变量准备,然后才把准备好的消息发送给用户空间。