kobject和kset

一、kobject

kobject是linux内核做的一个抽象,它本身不具备实际的含义。它被嵌入到各种数据结构中,因而只要我们具有kobject就可以获取并访问它所嵌入的宿主数据结构。这样就提供了良好的组织管理能力。宿主数据结构可以千变万化,但是其嵌入的kobject很简单,可以基于该结构做很多通用的事情。sysfs就是利用这一点实现的。该文件系统以很简单的方式实现了对内核中各种复杂部件(尤其是硬件相关部分)的统一管理。这非常类似于面向对象的思想,kobject相当于一个基类,其它类是它的派生类。。。
根据kobject所采用的思想,在使用该结构时,我们经常需要做的就是获取其宿主数据结构,这可以通过container_of宏来完成。

内嵌kobject的一个例子就是设备的基础数据结构struct device,它内部就包含了一个kobject数据结构。

对于sysfs来说,其中的每一个目录必定对应一个kobject对象。每个kobject的属性会成为sysfs中的一个文件。

1.1 数据结构

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;
};
  • name:该kobject的名字,会作为文件名出现在对应的sysfs中
  • entry:链表元素,用于将该kobject链接到它所属的kset的链表中。
  • parent:指向该对象的父对象
  • kset:如果该对象属于一个kset,则指向所属的kset
  • ktype:包含了类型信息,包括该对象的操作函数指针以及销毁该对象时的释放函数
  • sd:该对象对应的sysfs节点数据结构
  • kref:引用计数
几个比特域的含义是正如名字所示,分别表示kobject是否初始化了,是否在sysfs中,是否发送过KOBJ_ADD消息,是否发送过KOBJ_REMOVE消息,是否禁止发送uevent。
kobj_type的定义如下:
struct kobj_type {
	void (*release)(struct kobject *kobj);
	const struct sysfs_ops *sysfs_ops;
	struct attribute **default_attrs;
	const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
	const void *(*namespace)(struct kobject *kobj);
};
  • 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的定义如下所示:

struct sysfs_ops {
	ssize_t	(*show)(struct kobject *, struct attribute *,char *);
	ssize_t	(*store)(struct kobject *,struct attribute *,const char *, size_t);
	const void *(*namespace)(struct kobject *, const struct attribute *);
};
show:属性的读操作函数。attribute指向被操作的属性
store:属性的写操作函数。attribute指向被操作的属性

1.2. kobject 初始化

kobject的初始化比较复杂,它至少包含如下几个操作
  • 将整个kobject设置为0
  • 调用kobject_init来初始化该kobject,这时需要提供一个kobj_type,它包含了操作该kobject的信息以及销毁该kobject的函数
kobject的创建者还需要正确的设置kobject的kset域。

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是顶层类容器。kset也有自己内部的kobject,但这个内部kobject是由kset代码管理的,不允许其它用户直接访问。
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的数据结构如下所示:
struct kset {
	struct list_head list;
	spinlock_t list_lock;
	struct kobject kobj;
	const struct kset_uevent_ops *uevent_ops;
};
  • 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_create_and_add创建一个新的kset,它会同时完成创建和初始化的工作。
当你不再使用一个kset时,可以用kset_unregister()释放它。

2.3 kset的其它操作

2.3.1 引用计数

类似于kobject,可以通过函数kset_get和kset_put来管理kset的引用计数。

2.3.2 kset名字

kset的名字保存在其内嵌的kobject中。因此设置其名字就是调用kobject的名字设置函数。

三、热插拔

热插拔事件时内核发送到用户空间的通知,用于表明系统配置出现了变化。在kobject被创建和删除时都会产生该消息,热插拔消息一般由用户空间的一个进程处理(最新的都是uevent守护进程)。用户空间进程会根据收到的信息来做比如加载驱动、挂载分区等等事情。

3.1 数据结构

支持热插拔的数据结构是:
struct kset_uevent_ops {
	int (* const filter)(struct kset *kset, struct kobject *kobj);
	const char *(* const name)(struct kset *kset, struct kobject *kobj);
	int (* const uevent)(struct kset *kset, struct kobject *kobj,
		      struct kobj_uevent_env *env);
};
  • filter:该函数用来阻止kset中某个特定的kobject向用户控件发送uevent。如果函数结果返回0,则不会发送。
  • name:该函数是用来覆盖uevent发送到用户控件的kset名字。默认情况下,这个名字和kset的名字一样,但如果提供了name函数,这个名字会被覆盖。
  • uevent:该函数在uevent将被发送到用户空间时调用,可以在uevent中添加更多的环境变量。
kset中包含了指向该结构的指针

3.2 热插拔消息的产生

在kobject产生热插拔消息后,该消息会被kobject_uevent_env函数处理。
该函数会根据分层结构逐层向上查找第一个kset,找到一个kset后,就会利用该kset中的kset_uevent_ops来进行消息的过滤、环境变量准备,然后才把准备好的消息发送给用户空间。

猜你喜欢

转载自blog.csdn.net/goodluckwhh/article/details/18730913
今日推荐