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

https://blog.csdn.net/yj4231/article/details/7799245

https://www.cnblogs.com/xiaojiang1025/p/6193959.html

posted @ 2020-05-16 20:51  Action_er  阅读(2635)  评论(0编辑  收藏  举报