随着技术的不断进步,系统的拓扑结构也越来越复杂,对智能电源管理、热插拔的支持要求也越来越高,2.4内核已经难以满足这些需求。为适应这种形势的需要,Linux 2.6内核提供了全新的内核设备模型。
Kobject的核心功能是:保持一个引用计数,当该计数减为0时,自动释放(由本文所讲的kobject模块负责) Kobject所占用的meomry空间。这就决定了Kobject必须是动态分配的(只有这样才能动态释放)。 而Kobject大多数的使用场景,是内嵌在大型的数据结构中(如Kset、device_driver等),因此这些大型的数据结构,也必须是动态分配、动态释放的。那么释放的时机是什么呢?是内嵌的Kobject释放时。但是Kobject的释放是由Kobject模块自动完成的(在引用计数为0时),那么怎么一并释放包含自己的大型数据结构呢?
这时Ktype就派上用场了。我们知道,Ktype中的release回调函数负责释放Kobject(甚至是包含Kobject的数据结构)的内存空间,那么Ktype及其内部函数,是由谁实现呢?是由上层数据结构所在的模块!因为只有它,才清楚Kobject嵌在哪个数据结构中,并通过Kobject指针以及自身的数据结构类型,找到需要释放的上层数据结构的指针,然后释放它。
讲到这里,就清晰多了。所以,每一个内嵌Kobject的数据结构,例如kset、device、device_driver等等,都要实现一个Ktype,并定义其中的回调函数。同理,sysfs相关的操作也一样,必须经过ktype的中转,因为sysfs看到的是Kobject,而真正的文件操作的主体,是内嵌Kobject的上层数据结构!
1.0 kobject
这是一个结构体,所有驱动结构但必一个kobject ,如struct device struct device_driver struct bus_type
函数的作用,是对结构体的填冲与操作,linux内核有面向对象的思想,所以理解了结构体内的内容,就明白函数的作用,所以首要任务是理解结构体。
//include/linux/kobject.h
63 struct kobject {
64 const char *name;//1
65 struct list_head entry;//2
66 struct kobject *parent;//3
67 struct kset *kset;//4
68 struct kobj_type *ktype;//5
69 struct kernfs_node *sd; /* sysfs directory entry *///6
70 struct kref kref;//7 计算引用,每次调用必加1,
71 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
72 struct delayed_work release;
73 #endif
74 unsigned int state_initialized:1;
75 unsigned int state_in_sysfs:1;
76 unsigned int state_add_uevent_sent:1;
77 unsigned int state_remove_uevent_sent:1;
78 unsigned int uevent_suppress:1;
79 };
struct kobject
--64-->name表示kobject对象的名字,对应sysfs下的一个目录。
--65-->entry是kobject中插入的head_list结构,
--66-->parent是指向当前kobject父对象的指针,体现在sys结构中就是包含当前kobject对象的目录对象,
--67-->kset表示当前kobject对象所属的集合,
--68-->ktype表示当前kobject的类型。
--69-->sd用于表示VFS文件系统的目录项,是设备与文件之间的桥梁,sysfs中的符号链接就是通过kernfs_node内的联合体实现的。
--70-->kref是对kobject的引用计数,当引用计数为0时,就回调之前注册的release方法释放该对象。
--74-->state_initialized:1初始化标志位,在对象初始化时被置位,表示对象是否已经被初始化。
--75-->state_in_sysfs:1表示kobject对象在sysfs中的状态,在对应目录中被创建则置1,否则为0。
--76-->state_add_uevent_sent:1是添加设备的uevent事件是否发送标志,添加设备时会向用户空间发送uevent事件,请求新增设备。
--77-->state_remove_uevent_sent:1是删除设备的uevent事件是否发送标志,删除设备时会向用户空间发送uevent事件,请求卸载设备
1.1 kobject函数
kobject_init
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
static void kobject_init_internal(struct kobject *kobj)
/*
1确认kobj和ktype不为空
2如果该指针已经初始化过(判断kobj->state_initialized),打印错误提示及堆栈信息(但不是致命错误,所以还可以继续)
3初始化kobj内部的参数,包括引用计数、list、各种标志等
4根据输入参数,将ktype指针赋予kobj->ktype
*/
kobject_add
int kobject_add(struct kobject *kobj, struct kobject *parent,const char *fmt, ...)
static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs)
static int kobject_add_internal(struct kobject *kobj)
//我们先看一下kobject所表示的目录创建过程。这是在sysfs_create_dir()中完成的
static int create_dir(struct kobject *kobj)
int sysfs_create_dir(struct kobject * kobj)
static int populate_dir(struct kobject *kobj)
/*
1校验kobj以及kobj->name的合法性,若不合法打印错误信息并退出
2调用kobject_get增加该kobject的parent的引用计数(如果存在parent的话)
3如果存在kset(即kobj->kset不为空),则调用kobj_kset_join接口加入kset。同时,如果该kobject没有parent,却存在kset,则将它的parent设为kset(kset是一个特殊的kobject),并增加kset的引用计数
4通过create_dir接口,调用sysfs的相关接口,在sysfs下创建该kobject对应的目录
5如果创建失败,执行后续的回滚操作,否则将kobj->state_in_sysfs置为1
6kobj_kset_join,负责将kobj加入到对应kset的链表中。
*/
2.0 kset
/* include/linux/kobject.h, line 108 */
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);
};
/*
release,通过该回调函数,可以将包含该种类型kobject的数据结构的内存空间释放掉。
sysfs_ops,该种类型的Kobject的sysfs文件系统接口。
default_attrs,该种类型的Kobject的atrribute列表(所谓attribute,
就是sysfs文件系统中的一个文件)。将会在Kobject添加到内核时,一并注册到sysfs中。
child_ns_type/namespace,和文件系统(sysfs)的命名空间有关,这里不再详细说明。
*/