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