kobject,kset,子系統層次結構

kobject,kset,子系統層次結構


內核通常用kobject 結構將各個對象連接起來組成一個分層的結構體系。
parent 是指向另外一個kobject 結構(分層結構中上一層的節點)的指針,主要用途是在 sysfs 層次中定位對象.


 一個 kset 是嵌入到相同類型結構的 kobject 的集合。但 struct kobj_type 關注的是對象的類型,而struct kset 關心的是對象的集合,可認爲kset是kobjects 的頂層容器類。每個 kset 在內部包含自己的 kobject, 並可以用多種處理kobject 的方法處理kset。 kset總是在 sysfs 中出現; 一旦設置了 kset 並把它添加到系統中, 將在 sysfs 中創建一個目錄;kobjects 不必在 sysfs 中表示, 但kset中的每一個 kobject 成員都在sysfs中得到表述。

增加 kobject 到 kset 中去,通常是在kobject 創建時完成,其過程分爲3步:

(1)完成kobject的初始化,特別注意entry,parent的初始化。
(2)把kobject 的 kset 成員指向目標kset。
(3)將kobject 傳遞給下面的函數:

int kobject_add(struct kobject *kobj); /*函數可能失敗(返回一個負錯誤碼),程序應作出相應地反應*/

內核提供了一個組合函數:

extern int kobject_register(struct kobject *kobj); /*僅僅是一個 kobject_init 和 kobject_add 的結合,其他成員的初始化必須在之前手動完成*/

當把一個kobject從kset中刪除以清除引用時使用:
void
kobject_unregister(struct kobject *kobj); /*是 kobject_del 和 kobject_put 的結合*/

kset 在一個標準的內核鏈表中保存了它的子節點,在大部分情況下, 被包含的 kobjects 在它們的 parent 成員中保存指向 kset內嵌的 kobject的指針,關係如下:

圖表中的所有的被包含的 kobjects 實際上被嵌入在一些其他類型中, 甚至可能其他的 kset。

ksets 有類似於kobjects初始化和設置接口。

ksets 還有一個指針指向 kobj_type 結構來描述它包含的 kobject,這個類型優先於 kobject 自身中的 ktype 。因此在典型的應用中, 在 struct kobject 中的 ktype 成員被設爲 NULL, 而 kset 中的ktype是實際被使用的。
在新的內核裏, kset 不再包含一個子系統指針struct subsystem * subsys, 而且subsystem已經被kset取代。



子系統是對整個內核中一些高級部分的表述。子系統通常出現在 sysfs分層結構中的頂層,內核子系統包括 block_subsys(/sys/block 塊設備)、 devices_subsys(/sys/devices 核心設備層)以及內核已知的用於各種總線的特定子系統。

對於新的內核已經不再有subsystem數據結構了,用kset代替了。每個 kset 必須屬於一個子系統,子系統成員幫助內核在分層結構中定位 kset 。

/*子系統通常用以下的宏聲明:*//linux/lib/kobject.h
 185#define decl_subsys(_name,_type,_uevent_ops) /
 186struct kset _name##_subsys = { /
 187        .kobj = { .name = __stringify(_name) }, /
 188        .ktype = _type, /
 189        .uevent_ops =_uevent_ops, /
 190}


/*子系統的操作函數:*/
void subsystem_init(struct kset *s);
int subsystem_register(struct kset *s);
void subsystem_unregister(struct kset *s);
struct subsystem *subsys_get(struct kset *s)
void subsys_put(struct kset *s);

/*這些函數基本上是kset操作函數的包裝,以實現子系統的操作*/
如:void subsystem_init(struct kset *s);在/linux/lib/kobject.c
 659int subsystem_register(struct kset *s)
 660{
 661        return kset_register(s);
 662}



kobject 是在 sysfs 虛擬文件系統後的機制。對每個在 sysfs 中的目錄, 在內核中都會有一個 kobject 與之對應。每個 kobject 都輸出一個或多個屬性, 它在 kobject 的 sysfs 目錄中以文件的形式出現, 其中的內容由內核產生。
sysfs文件系統詳解:http://blog.chinaunix.net/u1/55599/showart_1089096.html
sysfs.h源碼詳析:http://blog.chinaunix.net/u1/55599/showart_1091002.html

在 sysfs 中創建kobject的入口是kobject_add的工作的一部分,只要調用 kobject_add 就會在sysfs 中顯示,還有些知識值得記住:
(1)kobjects 的 sysfs 入口始終爲目錄, kobject_add 的調用將在sysfs 中創建一個目錄,這個目錄包含一個或多個屬性(文件);
(2)分配給 kobject 的名字( 用 kobject_set_name ) 是 sysfs 中的目錄名,出現在 sysfs 層次的相同部分的 kobjects 必須有唯一的名字. 分配給 kobjects 的名字也應當是合法的文件名字: 它們不能包含非法字符(如:斜線)且不推薦使用空白。
(3)sysfs 入口位置對應 kobject 的 parent 指針。若 parent 是 NULL ,則它被設置爲嵌入到新 kobject 的 kset 中的 kobject;若 parent 和 kset 都是 NULL, 則sysfs 入口目錄在頂層目錄,通常不推薦。


當創建kobject 時, 每個 kobject 都被給定一系列默認屬性。這些屬性保存在 kobj_type 結構中:

struct kobj_type {
 void (*release)(struct kobject *);
 struct sysfs_ops *sysfs_ops;/*提供實現以下屬性的方法*/
 struct attribute **default_attrs; /*用於保存類型屬性列表(指針的指針) */
};

struct attribute {
 char *name;/*屬性的名字( 在 kobject 的 sysfs 目錄中顯示)*/
 struct module *owner;/*指向模塊的指針(如果有), 此模塊負責實現這個屬性*/
 mode_t mode; /*屬性的保護位,modes 的宏定義在 <linux/stat.h>:例如S_IRUGO 爲只讀屬性等等*/
}; /*default_attrs 列表中的最後一個元素必須用 0 填充*/

 sysfs 讀寫這些屬性是由 kobj_type->sysfs_ops 成員中的函數完成的:

struct sysfs_ops {
 ssize_t (*show)(struct kobject *kobj, struct attribute *attr, char *buffer);
 ssize_t (*store)(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size);
};

當用戶空間讀取一個屬性時,內核會使用指向 kobject 的指針(kobj)和正確的屬性結構(*attr)來調用show 方法,該方法將給定屬性值編碼進緩衝(buffer)(注意不要越界( PAGE_SIZE 字節)), 並返回實際數據長度。sysfs 的約定要求每個屬性應當包含一個可讀值; 若返回大量信息,需將它分爲多個屬性.

也可對所有 kobject 關聯的屬性使用同一個 show 方法,用傳遞到函數的 attr 指針來判斷所請求的屬性。有的 show 方法包含對屬性名字的檢查。有的show 方法會將屬性結構嵌入另一個結構, 這個結構包含需要返回屬性值的信息,這時可用container_of 獲得上層結構的指針以返回屬性值的信息。

store 方法將存在緩衝(buffer)的數據( size 爲數據的長度,不能超過 PAGE_SIZE )解碼並保存新值到屬性(*attr), 返回實際解碼的字節數。store 方法只在擁有屬性的寫權限時才能被調用。此時注意:接收來自用戶空間的數據一定要驗證其合法性。如果到數據不匹配, 返回一個負的錯誤值。


雖然 kobject 類型的 default_attrs 成員描述了所有的 kobject 會擁有的屬性,倘若想添加新屬性到 kobject 的 sysfs 目錄屬性只需簡單地填充一個attribute結構並傳遞到以下函數:

int sysfs_create_file(struct kobject *kobj, struct attribute *attr);
/*若成功,文件以attribute結構中的名字創建並返回 0; 否則, 返回負錯誤碼*/
/*注意:內核會調用相同的 show() 和 store() 函數來實現對新屬性的操作,所以在添加一個新非默認屬性前,應採取必要的步驟確保這些函數知道如何實現這個屬性*/

若要刪除屬性,調用:

int sysfs_remove_file(struct kobject *kobj, struct attribute *attr);
/*調用後, 這個屬性不再出現在 kobject 的 sysfs 入口。若一個用戶空間進程可能有一個打開的那個屬性的文件描述符,在這個屬性已經被刪除後,show 和 store 仍然可能被調用*/

二進制屬性

sysfs 通常要求所有屬性都只包含一個可讀文本格式的值,很少需要創建能夠處理大量二進制數據的屬性。但當在用戶空間和設備間傳遞不可改變的數據時(如上傳固件到設備)就需要這個特性。二進制屬性使用一個 bin_attribute 結構來描述:

struct bin_attribute {
    struct attribute    attr;/*屬性結構體*/
    size_t            size;/*這個二進制屬性的最大大小(若無最大值則爲0)*/
    void            *private;
    ssize_t (*read)(struct kobject *, char *, loff_t, size_t);
    ssize_t (*write)(struct kobject *, char *, loff_t, size_t);
/*read 和 write 方法類似字符驅動的讀寫方法;,在一次加載中可被多次調用,每次調用最大操作一頁數據,且必須能以其他方式判斷操作數據的末尾*/
    int (*mmap)(struct kobject *, struct bin_attribute *attr,
         struct vm_area_struct *vma);
};

/*二進制屬性必須顯式創建,不能以默認屬性被創建,創建一個二進制屬性調用:*/
int sysfs_create_bin_file(struct kobject *kobj, struct bin_attribute *attr);

/*刪除二進制屬性調用:*/
int sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);

符號鏈接

sysfs 文件系統具有樹型結構, 反映 kobject之間的組織層次關係。爲了表示驅動程序和所管理的設備間的關係,需要額外的指針,其在 sysfs 中通過符號鏈接實現。

/*在 sysfs 創建一個符號鏈接:*/
int sysfs_create_link(struct kobject *kobj, struct kobject *target, char *name);
/*函數創建一個鏈接(name)指向target的 sysfs 入口作爲 kobj 的一個屬性,是一個相對連接,與它在sysfs 系統中的位置無關*/

/*刪除符號連接調用:*/
void sysfs_remove_link(struct kobject *kobj, char *name);

熱插拔事件產生

一個熱插拔事件是一個從內核空間發送到用戶空間的通知, 表明系統配置已經改變. 無論 kobject 被創建或刪除,都會產生這種事件。熱插拔事件會導致對 /sbin/hotplug 的調用, 它通過加載驅動程序, 創建設備節點, 掛載分區或其他正確動作響應事件。
熱插拔事件的實際控制是通過一套存儲於 kset_uevent_ops (《LDD3》中介紹的struct kset_hotplug_ops * hotplug_ops;在2.6.22.2中已經被kset_uevent_ops 結構體替換)結構的方法完成:

struct kset_uevent_ops {
    int (*filter)(struct kset *kset, struct kobject *kobj);
    const char *(*name)(struct kset *kset, struct kobject *kobj);
    int (*uevent)(struct kset *kset, struct kobject *kobj, char **envp,
            int num_envp, char *buffer, int buffer_size);
};

可以在 kset 結構的uevent_ops 成員中找到指向kset_uevent_ops結構的指針。

若在 kobject 中不包含指定的 kset , 內核將通過 parent 指針在分層結構中進行搜索,直到發現一個包含有kset的 kobject ; 接着使用這個 kset 的熱插拔操作。
kset_uevent_ops 結構中的三個方法作用如下:
(1) filter 函數讓 kset 代碼決定是否將事件傳遞給用戶空間。如果 filter 返回 0,將不產生事件。以磁盤的 filter 函數爲例,它只允許kobject產生磁盤和分區的事件,源碼如下:

static int block_hotplug_filter(struct kset *kset, struct kobject *kobj)
{
 struct kobj_type *ktype = get_ktype(kobj);
    return ((ktype == &ktype_block) || (ktype == &ktype_part));
}

(2) 當調用用戶空間的熱插拔程序時,相關子系統的名字將作爲唯一的參數傳遞給它。name 函數負責返回合適的字符串傳遞給用戶空間的熱插拔程序。

(3)熱插拔腳本想得到的任何其他參數都通過環境變量傳遞。uevent 函數的作用是在調用熱插拔腳本之前將參數添加到環境變量中。函數原型:

int (*uevent)(struct kset *kset, struct kobject *kobj, /*產生事件的目標對象*/
 char **envp,/*一個保存其他環境變量定義(通常爲 NAME=value 的格式)的數組*/
 int num_envp, /*環境變量數組中包含的變量個數(數組大小)*/
 char *buffer, int buffer_size/*環境變量被編碼後放入的緩衝區的指針和字節數(大小)*/
/*若需要添加任何環境變量到 envp, 必須在最後的添加項後加一個 NULL 入口,使內核知道數組的結尾*/
        );
/*返回值正常應當是 0,若返回非零值將終止熱插拔事件的產生*/

熱插拔事件的產生通常是由在總線驅動程序層的邏輯所控制。

注:主要轉載來自 http://www.mcublog.com/blog/user1/2252/index.html
   經過一定的修改
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章