kobject和kset
阿新 • • 發佈:2018-11-05
一、kobject
kobject是linux核心做的一個抽象,它本身不具備實際的含義。它被嵌入到各種資料結構中,因而只要我們具有kobject就可以獲取並訪問它所嵌入的宿主資料結構。這樣就提供了良好的組織管理能力。宿主資料結構可以千變萬化,但是其嵌入的kobject很簡單,可以基於該結構做很多通用的事情。sysfs就是利用這一點實現的。該檔案系統以很簡單的方式實現了對核心中各種複雜部件(尤其是硬體相關部分)的統一管理。這非常類似於面向物件的思想,kobject相當於一個基類,其它類是它的派生類。。。根據kobject所採用的思想,在使用該結構時,我們經常需要做的就是獲取其宿主資料結構,這可以通過container_of巨集來完成。
內嵌kobject的一個例子就是裝置的基礎資料結構struct device,它內部就包含了一個kobject資料結構。
對於sysfs來說,其中的每一個目錄必定對應一個kobject物件。每個kobject的屬性會成為sysfs中的一個檔案。
1.1 資料結構
kobject被設計為可以反映出資料結構的層次關係,其定義如下所示:struct kobject { const char *name; struct list_head entry; struct kobject *parent; struct kset *kset; struct kobj_type *ktype; struct sysfs_dirent *sd; struct kref kref; unsigned int state_initialized:1; unsigned int state_in_sysfs:1; unsigned int state_add_uevent_sent:1; unsigned int state_remove_uevent_sent:1; unsigned int uevent_suppress:1; };
- name:該kobject的名字,會作為檔名出現在對應的sysfs中
- entry:連結串列元素,用於將該kobject連結到它所屬的kset的連結串列中。
- parent:指向該物件的父物件
- kset:如果該物件屬於一個kset,則指向所屬的kset
- ktype:包含了型別資訊,包括該物件的操作函式指標以及銷燬該物件時的釋放函式
- sd:該物件對應的sysfs節點資料結構
- kref:引用計數
kobj_type的定義如下:
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); };
- ktype用來表示一類kobject的公共內容,主要包括了對該kobject進行操作相關的函式指標資訊。每個kobject都要有一個對應的kobj_type結構,在kobject_init或者kobject_init_and_add呼叫時給出。
- release:函式指標,該類kobject的銷燬函式。
- sysfs_ops:包含了如何實現kobject的屬性資訊的函式指標
- default_attrs:預設屬性連結串列,這些屬性會在這類kobject建立時自動被建立(populate_dir中)。
struct sysfs_ops的定義如下所示:
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
const void *(*namespace)(struct kobject *, const struct attribute *);
};
show:屬性的讀操作函式。attribute指向被操作的屬性
store:屬性的寫操作函式。attribute指向被操作的屬性
1.2. kobject 初始化
kobject的初始化比較複雜,它至少包含如下幾個操作- 將整個kobject設定為0
- 呼叫kobject_init來初始化該kobject,這時需要提供一個kobj_type,它包含了操作該kobject的資訊以及銷燬該kobject的函式
1.3 kobject物件操作
1.3.1 將物件新增到sysfs
kobject_add用於將一個kobject新增到sysfs中,它需要呼叫者提供該物件的父物件以及該物件的名字作為引數。同時如果kobject需要和一個特定的kset關聯,在呼叫kobject_add之前要先設定該物件的kset。kobject_add會將kobject新增到kset中。如果一個kobject與一個kset關聯,那它的父節點可以設為NULL,這樣kobject的父節點會在呼叫kobject_add時自動設為kset內部的kobject。1.3.2 kobject物件改名
如果需要修改kobject的名字,需要呼叫kobject_rename來完成,而不是直接修改。如果要獲取kobject的名字可以使用函式kobject_name來獲取。1.3.3 通知userspace
在將kobject新增到sysfs後,可以通過kobject_uevent向userspace傳送通告通知,此時action引數為KOBJ_ADD。如果想通知userspace刪除一個物件,可以通過kobject_uevent向userspace傳送action引數為KOBJ_REMOVE的訊息。
需要注意的是,如果一個kobject以及它的祖先都不屬於某個kset,則核心就不會允許它傳送通告,因而如果要支援uevent,則核心部件必須屬於某個kset。
1.1.4 引用計數的操作
kobject的一個關鍵作用是它可以作為它所嵌入的宿主資料結構的引用計數。因為它包含了一個kref資料結構。kobject的引用計數操作API為kobject_get何kobject_put。kobject_get呼叫成功時會增加kobject中的kref的計數值,同時會返回指向kobject的指標。
kobject_put的呼叫會遞減kobject中的kref的引用計數值,,同時如果引用計數變為0,則會呼叫該kobject的ktype的release函式釋放該kobject物件。在刪除時,kobject還會檢查該物件是否向userspace傳送過KOBJ_ADD訊息,如果傳送過該訊息並且到此時還沒傳送過KOBJ_REMOVE訊息,則就會發送一個KOBJ_REMOVE訊息到userspace。這樣做是因為如果傳送過KOBJ_ADD訊息,則userspace的工具就會處理該訊息,在這個過程中應該有開啟相應sysfs檔案的動作,這就會導致應用計數增加,如果不傳送相應的KOBJ_REMOVE訊息,則相應的檔案的引用計數就無法減小,從而導致其它的問題。
從這裡也可以看出,kobject的釋放是非同步的,這是正常的,因為無法預期kobject的使用情況,也就無法確定何時釋放它,因而核心採用了一貫的做法,使用一個引用計數,當引用計數為0時就釋放。
1.1.5 建立簡單的kobject
有時開發者只想在sysfs中建立一個簡單的目錄,而且不想考慮與kset、show函式、store函式等一系列細節問題。這時就可以使用kobject_create_and_add來建立一個簡單的kobject。這個函式會建立一個kobject,並把它放在sysfs中parent的子目錄中。為了建立這個kobject的相關屬性,可以使用函式sysfs_create_file和sysfs_create_group。
1.4 kobject非預設屬性
預設情況下kobject的default_attrs成員描述了kobject擁有的所有屬性。但是kobject還提供了另外的機制用來新增新的屬性。如果想要為一個kobject新增預設屬性外的屬性,可以呼叫以下API: int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)呼叫完後,系統會使用屬性中的名字為該kobject的該屬性建立一個屬性檔案。不過需要注意的是這裡沒有提供新的kobject_type,也即沒有新的屬性操作函式,因此在新增新的屬性之前必須確保kobject的屬性操作函式知道如何處理新的屬性。 可以使用以下API將屬性刪除: void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
呼叫完該函式後,屬性檔案將從sysfs中消失,但是使用者可能還開啟有該屬性檔案,因而還有可能對其進行讀寫。
二、kset
kset是一些kobject的集合,這些kobject通過kset關聯在一起。一個kset中的kobject可以使用不同的ktype。一個kset有如下功能:
- 它作為一個容器,包含一組物件。它可以被核心用來跟蹤所有的塊裝置或者所有的PCI裝置驅動。
- 一個kset在sysfs中變現為一個分層的目錄。每個kset都有一個內部kobject,作為其它kobject的父目錄,其餘各個kobject都是它的子目錄。sysfs的頂層目錄結構也是這樣組成的。
- kset可以支援kobject的熱插拔,並且影響uevent事件如何被髮布到使用者空間。
kset用一個標準的連結串列來管理它的子kobject。kset的子kobject的kset域指向了該kset,kset的子kobject的parent會被設定為kset的內部kobject。不同於kobject的是ksets肯定會在sysfs 中出現,一旦建立了一個
kset並且將其新增到了系統中,則就會有一個相應的sysfs生成。kobject可能並不出現在sysfs中,但是kset的每個kobject成員都會出現在sysfs中。
2.1 kset的資料結構
kset的資料結構如下所示:struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};
- list:子kobject連結串列
- list_lock:保護連結串列的自旋鎖
- kobj:kset內嵌的kobject,kset的子kobject的parent會指向它。
- uevent_ops:該kset的uevent操作函式集
2.2 kset的初始化
kset的初始化類似於kobject的初始化,包括(可以參考函式__bus_register或者__class_register):- 分配資料結構
- 進行初始化,初始化kset內嵌的kobject
- 呼叫kset_register,它會呼叫kobject_init_internal和kobject_add_internal初始化kset的kobject,並且呼叫kobject_uevent發出KOBJ_ADD事件
當你不再使用一個kset時,可以用kset_unregister()釋放它。
2.3 kset的其它操作
2.3.1 引用計數
類似於kobject,可以通過函式kset_get和kset_put來管理kset的引用計數。2.3.2 kset名字
kset的名字儲存在其內嵌的kobject中。因此設定其名字就是呼叫kobject的名字設定函式。
三、熱插拔
熱插拔事件時核心傳送到使用者空間的通知,用於表明系統配置出現了變化。在kobject被建立和刪除時都會產生該訊息,熱插拔訊息一般由使用者空間的一個程序處理(最新的都是uevent守護程序)。使用者空間程序會根據收到的資訊來做比如載入驅動、掛載分割槽等等事情。3.1 資料結構
支援熱插拔的資料結構是:struct kset_uevent_ops {
int (* const filter)(struct kset *kset, struct kobject *kobj);
const char *(* const name)(struct kset *kset, struct kobject *kobj);
int (* const uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
- filter:該函式用來阻止kset中某個特定的kobject向用戶控制元件傳送uevent。如果函式結果返回0,則不會發送。
- name:該函式是用來覆蓋uevent傳送到使用者控制元件的kset名字。預設情況下,這個名字和kset的名字一樣,但如果提供了name函式,這個名字會被覆蓋。
- uevent:該函式在uevent將被髮送到使用者空間時呼叫,可以在uevent中新增更多的環境變數。
3.2 熱插拔訊息的產生
在kobject產生熱插拔訊息後,該訊息會被kobject_uevent_env函式處理。該函式會根據分層結構逐層向上查詢第一個kset,找到一個kset後,就會利用該kset中的kset_uevent_ops來進行訊息的過濾、環境變數準備,然後才把準備好的訊息傳送給使用者空間。