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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM