linux设备驱动(5)kobject kset 详解
1. 前言
在之前的device、device_driver和bus讲解中多次遇到kobject和kset相关内容,可能不明白,没关系,下面将详细讲解。
Kobject/kset是Linux设备驱动模型的基础,相当于数学中的加减乘除,后续一切模型都以此为基础构建。
2. 基本概念
由上一节可知,Linux设备模型的核心是使用Bus、Class、Device、Driver四个核心数据结构,将大量的、不同功能的硬件设备(以及驱动该硬件设备的方法),以树状结构的形式,进行归纳、抽象,从而方便Kernel的统一管理。
而硬件设备的数量、种类是非常多的,这就决定了Kernel中将会有大量的有关设备模型的数据结构。这些数据结构一定有一些共同的功能,需要抽象出来统一实现,否则就会不可避免的产生冗余代码。这就是Kobject诞生的背景。
通过parent指针,可以将所有Kobject以层次结构的形式组合起来。
使用一个引用计数(reference count),来记录Kobject被引用的次数,并在引用次数变为0时把它释放(这是Kobject诞生时的唯一功能)。
和sysfs虚拟文件系统配合,将每一个Kobject及其特性,以文件的形式,开放到用户空间(有关sysfs,会在其它文章中专门描述,本文不会涉及太多内容)。
注1:在Linux中,Kobject几乎不会单独存在。它的主要功能,就是内嵌在一个大型的数据结构中,为这个数据结构提供一些底层的功能实现。
注2:Linux driver开发者,很少会直接使用Kobject以及它提供的接口,而是使用构建在Kobject之上的设备模型接口。
3. 代码分析
源代码定义位于:
include/linux/kobject.h
lib/kobject.c
其中kobject.h为Kobject的头文件,包含所有的数据结构定义和接口声明。kobject.c为核心功能的实现。
3.1 主要的数据结构
在描述数据结构之前,有必要说明一下Kobject, Kset和Ktype这三个概念。
Kobject是基本数据类型,每个Kobject都会在"/sys/“文件系统中以目录的形式出现。
Ktype代表Kobject(严格地讲,是包含了Kobject的数据结构)的属性操作集合(由于通用性,多个Kobject可能共用同一个属性操作集,因此把Ktype独立出来了)。
Kset是一个特殊的Kobject(因此它也会在"/sys/“文件系统中以目录的形式出现),它用来集合相似的Kobject(这些Kobject可以是相同属性的,也可以不同属性的)
三者在内核中的低位和关系结构图:
3.1.1结构体kobject
kobject是一个对象的抽象,它用于管理对象。每个kobject对应着sysfs中的一个目录。
kobject用struct kobject来描述。
1 struct kobject { 2 const char *name;//在sysfs建立目录的名字 3 struct list_head entry;//链表,用于连接到所属kset的链表中 4 struct kobject *parent;//父对象 5 struct kset *kset;//属于哪个kset 6 struct kobj_type *ktype;//kobject的类型 7 struct sysfs_dirent *sd;//sysfs中与该对象对应的文件节点 8 struct kref kref;//该对象的引用计数,使用时加1,使用结束时减1,会0时会调用release释放。在include/linux/kref.h中定义,为一个可用于原子操作的引用计数 9 unsigned int state_initialized:1;//表示该Kobject是否已经初始化,以在Kobject的Init,Put,Add等操作时进行异常校验 10 unsigned int state_in_sysfs:1;//,表示该Kobject是否已在sysfs中呈现,以便在自动注销时从sysfs中移除。 11 unsigned int state_add_uevent_sent:1;//记录是否已经向用户空间发送ADD uevent,如果有,且没有发送remove uevent,则在自动注销时,补发REMOVE uevent,以便让用户空间正确处理。 12 unsigned int state_remove_uevent_sent:1; 13 unsigned int uevent_suppress:1;//如果该字段为1,则表示忽略所有上报的uevent事件 14 };
3.1.2结构体Kset
kset是一些kobject的集合,这些kobject可以有相同的ktype,也可以不同。同时,kset自己也包含一个kobject。在sysfs中,kset也是对应这一个目录,但是目录下面包含着其他的kojbect。
kset使用struct kset来描述。
1 /** 2 * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. 3 * 4 * A kset defines a group of kobjects. They can be individually 5 * different "types" but overall these kobjects all want to be grouped 6 * together and operated on in the same manner. ksets are used to 7 * define the attribute callbacks and other common events that happen to 8 * a kobject. 9 * 10 * @list: the list of all kobjects for this kset 11 * @list_lock: a lock for iterating over the kobjects 12 * @kobj: the embedded kobject for this kset (recursion, isn't it fun...) 13 * @uevent_ops: the set of uevent operations for this kset. These are 14 * called whenever a kobject has something happen to it so that the kset 15 * can add new environment variables, or filter out the uevents if so 16 * desired. 17 */ 18 struct kset { 19 struct list_head list;//该kset上的kobject链表 20 spinlock_t list_lock; 21 struct kobject kobj;//内嵌的kobject 22 const struct kset_uevent_ops *uevent_ops;//该kset的uevent操作函数集。当任何Kobject需要上报uevent时,都要调用它所从属的kset的uevent_ops,添加环境变量,或者过滤event(kset可以决定哪些event可以上报)。因此,如果一个kobject不属于任何kset时,是不允许发送uevent的。 23 };
1 struct kset_uevent_ops { 2 int (* const filter)(struct kset *kset, struct kobject *kobj); 3 const char *(* const name)(struct kset *kset, struct kobject *kobj); 4 int (* const uevent)(struct kset *kset, struct kobject *kobj, 5 struct kobj_uevent_env *env); 6 };
3.1.3kobj_type
每个kobject对象都内嵌有一个kobj_type类型的ktype,该结构定义了kobject在创建和删除时所采取的行为。
定义位于:include/linux/kobject.h 108行
1 struct kobj_type { 2 void (*release)(struct kobject *kobj);//当kobject的引用计数为0时,通过release方法来释放相关的资源。 3 const struct sysfs_ops *sysfs_ops;//sysfs_op的两个方法用于实现读取和写入属性文件时应该采取的行为。 4 struct attribute **default_attrs;//attribute为属性,每个属性在sysfs中都有对应的属性文件。所谓attribute,就是sysfs文件系统中的文件,将会在Kobject添加到内核时,一并注册到sysfs中 5 const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); 6 const void *(*namespace)(struct kobject *kobj);//文件系统(sysfs)的命名空间相关 7 }; 8 9 struct sysfs_ops { 10 ssize_t (*show)(struct kobject *, struct attribute *,char *);//读取属性文件 11 ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);//写入属性文件 12 const void *(*namespace)(struct kobject *, const struct attribute *); 13 };
3.1.4 kobject和kset关系总结
从sysfs角度而言,kset代表一个文件夹,而下面的kobj就是这个文件夹里面的内容,而内容有可能是文件也有可能是文件夹。
上图形象的描述了kobject和kset关系,最下面的kobj都属于一个kset,同时这些kobj的父对象就是kset内嵌的kobj。通过链表,kset可以获取所有属于它的kobj。
总结,Ktype以及整个Kobject机制的理解。
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的上层数据结构!
顺便提一下,Kobject是面向对象的思想在Linux kernel中的极致体现,但C语言的优势却不在这里,所以Linux kernel需要用比较巧妙(也很啰嗦)的手段去实现。
3.2kobject主要API分析
3.2.1 Kobject使用流程
Kobject大多数情况下(有一种例外,下面会讲)会嵌在其它数据结构中使用,其使用流程如下:
(1)定义一个struct kset类型的指针,并在初始化时为它分配空间,添加到内核中
(2)根据实际情况,定义自己所需的数据结构原型,该数据结构中包含有Kobject
(3)定义一个适合自己的ktype,并实现其中回调函数
(4)在需要使用到包含Kobject的数据结构时,动态分配该数据结构,并分配Kobject空间,添加到内核中。
(5)每一次引用数据结构时,调用kobject_get接口增加引用计数;引用结束时,调用kobject_put接口,减少引用计数
(6)当引用计数减少为0时,Kobject模块调用ktype所提供的release接口,释放上层数据结构以及Kobject的内存空间
(7)有一种例外,Kobject不再嵌在其它数据结构中,可以单独使用,这个例外就是:开发者只需要在sysfs中创建一个目录,而不需要其它的kset、ktype的操作。这时可以直接调用kobject_create_and_add接口,分配一个kobject结构并把它添加到kernel中。
3.2.2 Kobject_create
Kobject模块可以使用kobject_create自行分配空间,并内置了一个ktype(dynamic_kobj_ktype),用于在计数为0是释放空间。
1 /** 2 * kobject_create - create a struct kobject dynamically 3 * 4 * This function creates a kobject structure dynamically and sets it up 5 * to be a "dynamic" kobject with a default release function set up. 6 * 7 * If the kobject was not able to be created, NULL will be returned. 8 * The kobject structure returned from here must be cleaned up with a 9 * call to kobject_put() and not kfree(), as kobject_init() has 10 * already been called on this structure. 11 */ 12 struct kobject *kobject_create(void) 13 { 14 struct kobject *kobj; 15 16 kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);//动态分配一个kobject,使用结束时释放掉 17 if (!kobj) 18 return NULL; 19 20 kobject_init(kobj, &dynamic_kobj_ktype);//初始化kobject并绑定ktype 21 return kobj; 22 }
1 static struct kobj_type dynamic_kobj_ktype = { 2 .release = dynamic_kobj_release, 3 .sysfs_ops = &kobj_sysfs_ops, 4 }; 5 6 static void dynamic_kobj_release(struct kobject *kobj) 7 { 8 pr_debug("kobject: (%p): %s\n", kobj, __func__); 9 kfree(kobj);//释放掉kobject空间 10 } 11 12 const struct sysfs_ops kobj_sysfs_ops = { 13 .show = kobj_attr_show,//读 14 .store = kobj_attr_store,//写 15 };
3.2.2 Kobject_init
初始化kobject部分成员
1 /** 2 * kobject_init - initialize a kobject structure 3 * @kobj: pointer to the kobject to initialize 4 * @ktype: pointer to the ktype for this kobject. 5 * 6 * This function will properly initialize a kobject such that it can then 7 * be passed to the kobject_add() call. 8 * 9 * After this function is called, the kobject MUST be cleaned up by a call 10 * to kobject_put(), not by a call to kfree directly to ensure that all of 11 * the memory is cleaned up properly. 12 */ 13 void kobject_init(struct kobject *kobj, struct kobj_type *ktype) 14 { 15 char *err_str; 16 17 if (!kobj) {//检查,要初始化的kobject不能为空 18 err_str = "invalid kobject pointer!"; 19 goto error; 20 } 21 if (!ktype) {//检查初始化的kobject的ktype不能为空 22 err_str = "must have a ktype to be initialized properly!\n"; 23 goto error; 24 } 25 if (kobj->state_initialized) {//确定kobject没有被初始化过 26 /* do not error out as sometimes we can recover */ 27 printk(KERN_ERR "kobject (%p): tried to init an initialized " 28 "object, something is seriously wrong.\n", kobj); 29 dump_stack();//出错,dump栈中出错信息。 30 } 31 32 kobject_init_internal(kobj);//继续初始化 33 kobj->ktype = ktype;//将ktype赋值给kobject 34 return; 35 36 error: 37 printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str); 38 dump_stack(); 39 }
3.2.3 Kobject_init_internal
继续初始化kobject成员,标记一些标志位,并加入所属kset链表中。
1 static void kobject_init_internal(struct kobject *kobj) 2 { 3 if (!kobj)//确定kobj存在 4 return; 5 kref_init(&kobj->kref);//原子操作,对kref引用计数加1 6 INIT_LIST_HEAD(&kobj->entry);//初始化链表头 7 kobj->state_in_sysfs = 0;//标记还没呈现到sysfs 8 kobj->state_add_uevent_sent = 0;//标记还未户空间发送add事件 9 kobj->state_remove_uevent_sent = 0;//没有发送remove事件 10 kobj->state_initialized = 1;//标记被初始化过 11 }
3.2.4kobject_add
1 /** 2 * kobject_add - the main kobject add function 3 * @kobj: the kobject to add 4 * @parent: pointer to the parent of the kobject. 5 * @fmt: format to name the kobject with. 6 * 7 * The kobject name is set and added to the kobject hierarchy in this 8 * function. 9 * 10 * If @parent is set, then the parent of the @kobj will be set to it. 11 * If @parent is NULL, then the parent of the @kobj will be set to the 12 * kobject associted with the kset assigned to this kobject. If no kset 13 * is assigned to the kobject, then the kobject will be located in the 14 * root of the sysfs tree. 15 * 16 * If this function returns an error, kobject_put() must be called to 17 * properly clean up the memory associated with the object. 18 * Under no instance should the kobject that is passed to this function 19 * be directly freed with a call to kfree(), that can leak memory. 20 * 21 * Note, no "add" uevent will be created with this call, the caller should set 22 * up all of the necessary sysfs files for the object and then call 23 * kobject_uevent() with the UEVENT_ADD parameter to ensure that 24 * userspace is properly notified of this kobject's creation. 25 */ 26 int kobject_add(struct kobject *kobj, struct kobject *parent, 27 const char *fmt, ...) 28 { 29 va_list args; 30 int retval; 31 32 if (!kobj) 33 return -EINVAL; 34 35 if (!kobj->state_initialized) {//确认未被初始化,否则出错dump数据返回错误 36 printk(KERN_ERR "kobject '%s' (%p): tried to add an " 37 "uninitialized object, something is seriously wrong.\n", 38 kobject_name(kobj), kobj); 39 dump_stack(); 40 return -EINVAL; 41 } 42 va_start(args, fmt); 43 retval = kobject_add_varg(kobj, parent, fmt, args);//添加? 44 va_end(args); 45 46 return retval; 47 }
va_start的用法见:va_list ,va_start ,va_arg ,va_copy ,va_end ,vsprintf ,vsnprintf 详细解析
3.2.5kobject_add_varg
解析格式化字符串,将结果赋予kobj->name,之后调用kobject_add_internal接口,完成真正的添加操作。
1 static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, 2 const char *fmt, va_list vargs) 3 { 4 int retval; 5 6 retval = kobject_set_name_vargs(kobj, fmt, vargs); 7 if (retval) { 8 printk(KERN_ERR "kobject: can not set name properly!\n"); 9 return retval; 10 } 11 kobj->parent = parent; 12 return kobject_add_internal(kobj); 13 }
3.2.5 kobject_add_internal
将新的kobject加入内核,将在sysfs中建立目录。
1 static int kobject_add_internal(struct kobject *kobj) 2 { 3 int error = 0; 4 struct kobject *parent; 5 6 if (!kobj)//存在有效 7 return -ENOENT; 8 9 if (!kobj->name || !kobj->name[0]) {//存在名字,要不然下面建立的目录没有名字 10 WARN(1, "kobject: (%p): attempted to be registered with empty " 11 "name!\n", kobj); 12 return -EINVAL; 13 } 14 15 parent = kobject_get(kobj->parent);//获取父节点,并把父节点引用计数加1 16 17 /* join kset if set, use it as parent if we do not already have one */ 18 if (kobj->kset) {//kset是kobj的集合,确认kobject中的kset存在 19 if (!parent)//如果该kobj的父节点不存在,就让kset里面的kobj做它的父节点 20 parent = kobject_get(&kobj->kset->kobj); 21 kobj_kset_join(kobj);//把kobj加入到kset list的链表中去 22 kobj->parent = parent;//给kobj添加父节点 23 } 24 25 pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n", 26 kobject_name(kobj), kobj, __func__, 27 parent ? kobject_name(parent) : "<NULL>", 28 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>"); 29 30 error = create_dir(kobj);//调用sysfs文件系统接口,创建一个名为kobj->name的文件夹 ,目录和parent有直接关系 31 if (error) { 32 kobj_kset_leave(kobj);//删除链表项 33 kobject_put(parent);//父节点引用计数减1 34 kobj->parent = NULL; 35 36 /* be noisy on error issues */ 37 if (error == -EEXIST) 38 WARN(1, "%s failed for %s with " 39 "-EEXIST, don't try to register things with " 40 "the same name in the same directory.\n", 41 __func__, kobject_name(kobj)); 42 else 43 WARN(1, "%s failed for %s (error: %d parent: %s)\n", 44 __func__, kobject_name(kobj), error, 45 parent ? kobject_name(parent) : "'none'"); 46 } else 47 kobj->state_in_sysfs = 1; 48 49 return error; 50 }
1 /* add the kobject to its kset's list */ 2 static void kobj_kset_join(struct kobject *kobj) 3 { 4 if (!kobj->kset) 5 return; 6 7 kset_get(kobj->kset); 8 spin_lock(&kobj->kset->list_lock); 9 list_add_tail(&kobj->entry, &kobj->kset->list); 10 spin_unlock(&kobj->kset->list_lock); 11 }
总结主要做三件事情:
(1)21 line,将kobject加入所属的kset链表中。
(2)22 line,指定kobject的父节点,父节点嵌入(1)所属的kset中,输入同一级。
(3)30 line,在sys对应目录下创建目录,名字为kobject的name。
3.2.6kobject_create_and_add
以上kobject_creat和kobject_add和结合
1 /** 2 * kobject_create_and_add - create a struct kobject dynamically and register it with sysfs 3 * 4 * @name: the name for the kobject 5 * @parent: the parent kobject of this kobject, if any. 6 * 7 * This function creates a kobject structure dynamically and registers it 8 * with sysfs. When you are finished with this structure, call 9 * kobject_put() and the structure will be dynamically freed when 10 * it is no longer being used. 11 * 12 * If the kobject was not able to be created, NULL will be returned. 13 */ 14 struct kobject *kobject_create_and_add(const char *name, struct kobject *parent) 15 { 16 struct kobject *kobj; 17 int retval; 18 19 kobj = kobject_create(); 20 if (!kobj) 21 return NULL; 22 23 retval = kobject_add(kobj, parent, "%s", name); 24 if (retval) { 25 printk(KERN_WARNING "%s: kobject_add error: %d\n", 26 __func__, retval); 27 kobject_put(kobj); 28 kobj = NULL; 29 } 30 return kobj; 31 }
3.2.7kobject_init_and_add
以上kobject_init和kobject_add和结合
1 /** 2 * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy 3 * @kobj: pointer to the kobject to initialize 4 * @ktype: pointer to the ktype for this kobject. 5 * @parent: pointer to the parent of this kobject. 6 * @fmt: the name of the kobject. 7 * 8 * This function combines the call to kobject_init() and 9 * kobject_add(). The same type of error handling after a call to 10 * kobject_add() and kobject lifetime rules are the same here. 11 */ 12 int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, 13 struct kobject *parent, const char *fmt, ...) 14 { 15 va_list args; 16 int retval; 17 18 kobject_init(kobj, ktype); 19 20 va_start(args, fmt); 21 retval = kobject_add_varg(kobj, parent, fmt, args); 22 va_end(args); 23 24 return retval; 25 }
3.2.8 kobject相API调用关系总结
kobject_creat(void)
kzalloc();//分配创建kobj
kobject_init(kobj, &dynamic_kobj_ktype);
kobject_init(struct kobject *kobj, struct kobj_type *ktype)
确定kobj和ktype非空 && kobj->state_initialized是否初始化过;
kobject_init_internal();
kref_init(&kobj->kref); 原子操作,对kref引用计数加1
初始化其他成员
INIT_LIST_HEAD(&kobj->entry); 初始化链表
kobject_add()
检查kobj kobj->state_initialized;
kobject_add_varg();
kobject_set_name_vargs();//kset_create调用的接口
kobject_add_internal();
parent = kobject_get(kobj->parent); //如果有父节点的话,获取父节点且引用计数加1
kobject_get();
kobj_kset_join(kobj); /* 把kobj加入到kset list的链表中去 */
kset_get(kobj->kset);
list_add_tail(&kobj->entry, &kobj->kset->list);//将kobj的entey加入到kset链表中
kobj->parent = parent; /* 给kobj添加父节点 */
create_dir(kobj);/创建对应目录
kobject_create_and_add(const char *name, struct kobject *parent)
kobject_create();
kobject_add();
kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,struct kobject *parent, const char *fmt, ...)
kobject_init(kobj, ktype);
kobject_add_varg(kobj, parent, fmt, args);
3.3kset相关API
3.3.1kset_create
最终还是调用kobject接口,kobject_set_name_vargs();
1 /** 2 * kset_create - create a struct kset dynamically 3 * 4 * @name: the name for the kset 5 * @uevent_ops: a struct kset_uevent_ops for the kset 6 * @parent_kobj: the parent kobject of this kset, if any. 7 * 8 * This function creates a kset structure dynamically. This structure can 9 * then be registered with the system and show up in sysfs with a call to 10 * kset_register(). When you are finished with this structure, if 11 * kset_register() has been called, call kset_unregister() and the 12 * structure will be dynamically freed when it is no longer being used. 13 * 14 * If the kset was not able to be created, NULL will be returned. 15 */ 16 static struct kset *kset_create(const char *name, 17 const struct kset_uevent_ops *uevent_ops, 18 struct kobject *parent_kobj) 19 { 20 struct kset *kset; 21 int retval; 22 23 kset = kzalloc(sizeof(*kset), GFP_KERNEL); 24 if (!kset) 25 return NULL; 26 retval = kobject_set_name(&kset->kobj, name);//解析格式化字符串,将结果赋予koset->kobj->name 27 if (retval) { 28 kfree(kset); 29 return NULL; 30 } 31 kset->uevent_ops = uevent_ops; 32 kset->kobj.parent = parent_kobj; 33 34 /* 35 * The kobject of this kset will have a type of kset_ktype and belong to 36 * no kset itself. That way we can properly free it when it is 37 * finished being used. 38 */ 39 kset->kobj.ktype = &kset_ktype; 40 kset->kobj.kset = NULL; 41 42 return kset; 43 }
1 /** 2 * kobject_set_name - Set the name of a kobject 3 * @kobj: struct kobject to set the name of 4 * @fmt: format string used to build the name 5 * 6 * This sets the name of the kobject. If you have already added the 7 * kobject to the system, you must call kobject_rename() in order to 8 * change the name of the kobject. 9 */ 10 int kobject_set_name(struct kobject *kobj, const char *fmt, ...) 11 { 12 va_list vargs; 13 int retval; 14 15 va_start(vargs, fmt); 16 retval = kobject_set_name_vargs(kobj, fmt, vargs);//调用上面kobject的接口 17 va_end(vargs); 18 19 return retval; 20 }
3.3.2kset_init
最终还是call kobject的接口kobject_init_internal实现初始化,初始化kset的链表。
需要注意的时,如果使用此接口,上层软件必须提供该kset中的kobject的ktype。
1 /** 2 * kset_init - initialize a kset for use 3 * @k: kset 4 */ 5 void kset_init(struct kset *k) 6 { 7 kobject_init_internal(&k->kobj);//初始化kobject的某些字段 8 INIT_LIST_HEAD(&k->list);//初始化kset用于组织下面kobj的链表头 9 spin_lock_init(&k->list_lock);//初始化kset的自旋锁 10 }
3.3.3kset_register
先kset init,然后再call kobject的kobject_add_internal将kset加内核
1 /** 2 * kset_register - initialize and add a kset. 3 * @k: kset. 4 */ 5 int kset_register(struct kset *k) 6 { 7 int err; 8 9 if (!k) 10 return -EINVAL; 11 12 kset_init(k);//初始化kset 13 err = kobject_add_internal(&k->kobj);//在sysfs中建立目录 14 if (err) 15 return err; 16 kobject_uevent(&k->kobj, KOBJ_ADD); 17 return 0; 18 }
3.3.4kset_create_and_add
kset_create和kset_register的组合
1 /** 2 * kset_create_and_add - create a struct kset dynamically and add it to sysfs 3 * 4 * @name: the name for the kset 5 * @uevent_ops: a struct kset_uevent_ops for the kset 6 * @parent_kobj: the parent kobject of this kset, if any. 7 * 8 * This function creates a kset structure dynamically and registers it 9 * with sysfs. When you are finished with this structure, call 10 * kset_unregister() and the structure will be dynamically freed when it 11 * is no longer being used. 12 * 13 * If the kset was not able to be created, NULL will be returned. 14 */ 15 struct kset *kset_create_and_add(const char *name, 16 const struct kset_uevent_ops *uevent_ops, 17 struct kobject *parent_kobj) 18 { 19 struct kset *kset; 20 int error; 21 22 kset = kset_create(name, uevent_ops, parent_kobj); 23 if (!kset) 24 return NULL; 25 error = kset_register(kset); 26 if (error) { 27 kfree(kset); 28 return NULL; 29 } 30 return kset; 31 }
3.3.5kset API 调用流程总结
kset_init()
kobject_init_internal(); /* 初始化里面的kobj ,kobject公用接口*/
INIT_LIST_HEAD(); /* 初始化kset用于组织下面kobj的链表 */
kset_create()
kset = kzalloc(sizeof(*kset), GFP_KERNEL); /* 申请一个kset */
kobject_set_name();
kobject_set_name_vargs(); //kobject公用接口
kset其他成员赋值;
kset_register()
kset_init(); /* 初始化kset */
kobject_add_internal(); /* 把kset的kobj加入到内核(创建一个文件夹),kobject公用接口
kobject_uevent(); //kobject公用接口
kset_create_and_add()
kset_create();
kset_register();
4 kobject 和kset的关系总结
每一个kobj对应文件系统 /sys里的一个目录,而每一个kset都包含了一个kobj,所以,kset也对应于/sys里的一个目录。简单来说,kset与kobj 都是目录,既然是目录,那么在就是一个树状结构,每一个目录都将有一个父节点。
(1)在kset中使用kset.kobj->parent指向父节点的kobj,进而找到kset。kset和kset->kobj是同一级,结构体kset通过list成员找到子节点kobj,通过kset->kobj找到父节点。
(2)在kboject中使用 kobj->parent找到父节点kobj,通过kset成员找到所属的kset,通过entry加入到所属的kset的kset->list链表中。
显然,整个树状目录结构,都是通过kobj来构建的,只不过有些kobj嵌在ket里,分析目录结构时把kset当成一个普通的kobj会好理解很多。
再看一下三者总体的关系。
上面这个图是超级经典的,指的反复学和思考树形目录的实现原理。
kobject是隐藏在sysfs虚拟文件系统后的机制,对于sysfs中的每一个目录,内核中都会存在一个对应的kobject。每一个kobject都输出一个或者多个属性,它们在kobject的sysfs目录中表现为文件,其中的内容由内核生成。
kobject在sysfs中始终是一个目录,这个目录包含一个或者多个属性。
分配给kobject的名字,是sysfs中的目录名字。sysfs的入口目录的位置对应于kobject的parent指针。调用kobject_add的时候,如果parent为NULL,它将被设置为嵌入到心得kobject的kset中的kobject,这样,sysfs 分层结构通常与kset创建的内部结构相匹配。如果parent和kset都是null,则会在最高层创建目录。
kobject里面可以放kobject,但上层的kobject不能联系下层,只能下层联系上层。
kset下可以放kset,上层可以通过kset里面的list链表连接下层kset里kobject里的list连接
kset下可以放kobj,上层可以通过kset里面的list链表连接下层kobject里的list连接
kobject里的kset,但上层的kobject不能联系下层,只能下层联系上层。
同时在驱动层次注册device或driver的时候会为其kobj(因为kobject单独存在无意义,通常都是和dev结合的)绑定kobj_type(大多时候都只是show和store接口)。大多数情况下kobject结构体都是作为基类被放置在某个device或driver里面的,所有释放device或driver的时候会整体释放。
参考博文:
https://blog.csdn.net/qq_16777851/java/article/details/81368580