Kobject模型

轉載自:http://blog.chinaunix.net/uid-11319766-id-3253414.html
這篇文章太重要了,所以必須轉載。感謝作者
一、Kobject
每個在內核中註冊的kobject都對應於sysfs文件系統中的一個目錄。
kobject在文件include/linux/kobject中定義
  1. struct kobject {  
  2.     const char        *name;                    //kobject的名稱  
  3.     struct list_head    entry;            //kobject結構鏈表  
  4.     struct kobject        *parent;        //父kobject結構體  
  5.     struct kset        *kset;                    //kset集合  
  6.     struct kobj_type    *ktype;            //kobject的類型描述符  
  7.     struct sysfs_dirent    *sd;            //sysfs文件目錄  
  8.     struct kref        kref;                        //kobject引用計數  
  9.     unsigned int state_initialized:1;    //kobject是否初始化  
  10.     unsigned int state_in_sysfs:1;    //是否已經加入sysfs  
  11.     unsigned int state_add_uevent_sent:1;  
  12.     unsigned int state_remove_uevent_sent:1;  
  13.     unsigned int uevent_suppress:1;  
  14. };  

sysfs組織結構,進入sysfs目錄中。有block bus class dev devices firmware fs kernel module power這些目錄。具體代表看名字差不多就可以看出。在層次結構上,假如有一個設備A。將有一個名稱爲A的目錄。A設備是在B總線上。那A設備應該在bus目錄下的B總線下。A設備肯定會有設備的屬性(ktype),例如是音頻設備則應該有音量屬性,則音量屬性將在A設備目錄下有個音量屬性文件。在使用設備時,如果要改變音量大小,則可以寫屬性文件入音量指。得到音量大小時,可以讀取屬性文件中的音量值。

 二。Kobject初始化
初始化一個kobject結構體變量,kobject_init函數(lib/kobject.c),調用此函數前應先將kobject變量成員全部置0
  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.     if (!kobj) {    //檢查kobj變量是否爲空  
  17.         err_str = "invalid kobject pointer!";  
  18.         goto error;  
  19.     }  
  20.     if (!ktype) {    //檢查ktype類型變量是否爲空  
  21.         err_str = "must have a ktype to be initialized properly!\n";  
  22.         goto error;  
  23.     }  
  24.     if (kobj->state_initialized) {    //是否已經初始化過  
  25.         /* do not error out as sometimes we can recover */  
  26.         printk(KERN_ERR "kobject (%p): tried to init an initialized "  
  27.          "object, something is seriously wrong.\n", kobj);  
  28.         dump_stack();  
  29.     }  
  30.     kobject_init_internal(kobj);    //進一步初始化kobj內部成員  
  31.     kobj->ktype = ktype;    //將參數中傳來的ktype變量賦值給kobj的ktype變量。  
  32.     return;  
  33. error:  
  34.     printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);  
  35.     dump_stack();  
  36. }  

分析kobject_init_internal函數(lib/kobject.c),此函數主要設置一些kobj中的一些變量
  1. static void kobject_init_internal(struct kobject *kobj)  
  2. {  
  3.     if (!kobj)    //kobj是否爲空  
  4.         return;  
  5.     kref_init(&kobj->kref);    //增加kobject的引用計數,kref_set(kref, 1);  
  6.     INIT_LIST_HEAD(&kobj->entry);    //初始化kobj的鏈表  
  7.     kobj->state_in_sysfs = 0;    //kobject還沒有註冊到sysfs中  
  8.     kobj->state_add_uevent_sent = 0;    //  
  9.     kobj->state_remove_uevent_sent = 0;  
  10.     kobj->state_initialized = 1;  
  11. }  


三。kobj_type
對象的屬性結構體kobj_type(include/linux/kobject.h)
  1. struct kobj_type {  
  2.     void (*release)(struct kobject *kobj);    //釋放函數(驅動編寫時提供),此函數會被kobject_put函數調用  
  3.     struct sysfs_ops *sysfs_ops;    //屬性文件的操作函數(只有讀和寫操作)  
  4.     struct attribute **default_attrs;    //屬性數組  
  5. };  


1.討論kobj_type和kobject的關係,就要先說說kobject的引用。引用一個kobject使用函數kobject_get()這個函數會增加kobject的引用並返回kobject的指針。增加其引用是通過其kobject中斷哦kref變量完成的。對kobject的引用管理主要是爲了知道被引用的情況,如引用不爲0就不能銷燬kobject對象,引用爲0時則調用相應的釋放函數等。
  1. struct kobject *kobject_get(struct kobject *kobj)  
  2. {  
  3.     if (kobj)  
  4.         kref_get(&kobj->kref);  
  5.     return kobj;  
  6. }  
  7.   
  8. void kref_get(struct kref *kref)  
  9. {  
  10.     WARN_ON(!atomic_read(&kref->refcount));  
  11.     atomic_inc(&kref->refcount);    //將kref中的這個原子變量加1  
  12.     smp_mb__after_atomic_inc();  
  13. }  
減少一個kobject對象的引用使用函數kobject_put()。當一個kobject對象的引用被減少到0時,程序就應該釋放這個kobject相關的資源。所以在減少引用的函數中就應該有調用釋放資源的相關代碼,在下面內核代碼中我們可以看到。
  1. void kobject_put(struct kobject *kobj)  
  2. {  
  3.     if (kobj) {  
  4.         if (!kobj->state_initialized)    //若kobj沒有初始化就不能減少其引用  
  5.             WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "  
  6.              "initialized, yet kobject_put() is being "  
  7.              "called.\n", kobject_name(kobj), kobj);  
  8.         kref_put(&kobj->kref, kobject_release); //減少kref計數  
  9.     }  
  10. }  
  11. int kref_put(struct kref *kref, void (*release)(struct kref *kref))  
  12. {  
  13.     WARN_ON(release == NULL);    //爲空警告  
  14.     WARN_ON(release == (void (*)(struct kref *))kfree); //如果release函數就是kfree,則警告(即release函數不能是簡單的kfree)  
  15.     if (atomic_dec_and_test(&kref->refcount)) {    //遞減原子變量並檢查其值  
  16.         release(kref);    //回調release函數  
  17.         return 1;  
  18.     }  
  19.     return 0;  
  20. }  
那這個release函數在哪裏保存呢,這就和kobj_type結構有關係了。上面我們可以看到kobj_type中有一個release函數指針,就是保存在這裏。每一個kobject的ktype都指向一個kobj_type,它保存了這個kobject類型的release函數指針。

四。Kset集合
1.Kset是具有相同類型的kobject集合。一個Kset集合可以表示在/sys/drivers/目錄下,表示一類驅動程序。kobject則表示一個具體的驅動目錄。這樣kset則可以將kobject組織成層次化結構。
  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。這樣kset本身也是一個kobject也被表現爲一個目錄  
  22.     struct kset_uevent_ops *uevent_ops;    //支持熱插拔事件的函數集  
  23. };  
kset中的kobject對象,所有屬於這個kset集合的kobject對象的parent指針,均指向這個內嵌的kobject,也即表示在一個kset集合中的kobject是相同類型的他們有相同的parent對象。kset的引用計數也就是內嵌的kobject的引用計數。
所以kobject和kset的關係簡單來講,就是
1.kset是kobject的一個頂層容器,它包含了相同類型的kobject,kset中有鏈表成員保存所有的kobject指向。
2.kobject中的kset指針指向了一個kset
3.kset中有kobject對象,表明了kset也可以有kobject相關的操作。
4.kset鏈表中的kobject對象的parent指針一般都指向kset內嵌的kobject對象。
kset和kobject關係基本如下圖,

kset_uevent_ops熱插拔事件
熱插拔事件是用內核空間發送到用戶空間的通知。表明內核中的某些配置已經發生變化。用戶空間則會根據這些信息做相應的處理。例如,U盤插入USB系統時,會產生一個熱插拔事件,內核會捕捉到這個熱插拔事件,然後調用/sbin/hotplug程序,該程序通知加載驅動程序來相應U盤的插入動作。
熱插拔函數集的定義在include/linux/koject.h中
  1. struct kset_uevent_ops {  
  2.     int (*filter)(struct kset *kset, struct kobject *kobj);        //事件過濾函數  
  3.     const char *(*name)(struct kset *kset, struct kobject *kobj);    //事件名稱函數  
  4.     int (*uevent)(struct kset *kset, struct kobject *kobj,struct kobj_uevent_env *env);  
  5.         //uevent函數可在熱插拔程序執行前,向環境變量寫值  
  6. };  
詳細的熱插拔事件先不在這裏分析。

2.註冊一個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.     if (!k)  
  9.         return -EINVAL;  
  10.     kset_init(k);  
  11.     err = kobject_add_internal(&k->kobj);    //將kset中的kobject添加進sysfs,函數將在後面講解  
  12.     if (err)  
  13.         return err;  
  14.     kobject_uevent(&k->kobj, KOBJ_ADD);  
  15.     return 0;  
  16. }  
  17. /** 
  18.  * kset_init - initialize a kset for use 
  19.  * @k: kset 
  20.  */  
  21. void kset_init(struct kset *k)  
  22. {  
  23.     kobject_init_internal(&k->kobj);  
  24.     INIT_LIST_HEAD(&k->list);  
  25.     spin_lock_init(&k->list_lock);  
  26. }  
我們使用函數kset_create_and_add()還可以一次性將kset創建並註冊進sysyfs
  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.                  struct kset_uevent_ops *uevent_ops,  
  17.                  struct kobject *parent_kobj)  
  18. {  
  19.     struct kset *kset;  
  20.     int error;  
  21.     kset = kset_create(name, uevent_ops, parent_kobj);    //根據參數創建一個kset  
  22.     if (!kset)  
  23.         return NULL;  
  24.     error = kset_register(kset);                //將kset註冊進sysfs,函數在上面已經分析過  
  25.     if (error) {  
  26.         kfree(kset);  
  27.         return NULL;  
  28.     }  
  29.     return kset;  
  30. }  
  1. /** 
  2.  
  3.      * kset_create - create a struct kset dynamically 
  4.      * 
  5.      * @name: the name for the kset 
  6.      * @uevent_ops: a struct kset_uevent_ops for the kset 
  7.      * @parent_kobj: the parent kobject of this kset, if any. 
  8.      * 
  9.      * This function creates a kset structure dynamically. This structure can 
  10.      * then be registered with the system and show up in sysfs with a call to 
  11.      * kset_register(). When you are finished with this structure, if 
  12.      * kset_register() has been called, call kset_unregister() and the 
  13.      * structure will be dynamically freed when it is no longer being used. 
  14.      * 
  15.      * If the kset was not able to be created, NULL will be returned. 
  16.      */  
  17.     static struct kset *kset_create(const char *name,  
  18.                     struct kset_uevent_ops *uevent_ops,  
  19.                     struct kobject *parent_kobj)  
  20.     {  
  21.         struct kset *kset;  
  22.         int retval;  
  23.         kset = kzalloc(sizeof(*kset), GFP_KERNEL);  
  24.         if (!kset)  
  25.             return NULL;  
  26.         retval = kobject_set_name(&kset->kobj, name);    //設置kobject名稱  
  27.         if (retval) {  
  28.             kfree(kset);  
  29.             return NULL;  
  30.         }  
  31.         kset->uevent_ops = uevent_ops;  
  32.         kset->kobj.parent = parent_kobj;            //設置kset的kobject的父對象  
  33.         /* 
  34.          * The kobject of this kset will have a type of kset_ktype and belong to 
  35.          * no kset itself. That way we can properly free it when it is 
  36.          * finished being used. 
  37.          */  
  38.         kset->kobj.ktype = &kset_ktype;    //設置kset的kobject的默認屬性  
  39.         kset->kobj.kset = NULL;  
  40.         return kset;  
  41.     }  

上面這些函數主要包含即調用關係如下,
kset_create_and_add
kset_create
kzalloc
kset_register
kset_init
kobject_add_internal

kset中嵌入了一個kobject,所以還有一些和kobject相似的函數如,
增加kset的引用,實際是調用kobject_get增加kset中的kobject的引用
  1. static inline struct kset *kset_get(struct kset *k)  
  2. {  
  3.     return k ? to_kset(kobject_get(&k->kobj)) : NULL;      
  4. }  

減少kset的引用,實際是調用kobject_put減少kset中的kobject的引用
  1. static inline void kset_put(struct kset *k)  
  2. {  
  3.     kobject_put(&k->kobj);  
  4. }  

順便提一下,子系統subsystem,在新的內核中已經沒有這個結構了。在原來的內核中它用來表示比kset更高一層的容器,kset應該屬於一個子系統,子系統幫助內核在分層結構中定位kset。內核子系統包括 block_subsys(/sys/block 塊設備)、 devices_subsys(/sys/devices 核心設備層)。現在subsystem已經被kset代替了。

五,將kobject註冊進sysfs系統
我們在看一下如何將kobject註冊進sysfs系統中。使用函數kobject_init_and_add()(lib/kobject.c)函數將一個kobject註冊進sysfs系統,在/sys中表現爲生成一個相應的目錄。
  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.     kobject_init(kobj, ktype);            //調用初始化函數先初始化kobject變量  
  18.     va_start(args, fmt);            //解析可變參數列表  
  19.     retval = kobject_add_varg(kobj, parent, fmt, args); //給kobject添加參數,並且將其添加到sysfs系統。  
  20.     va_end(args);                            //結束解析參數列表  
  21.     return retval;  
  22. }  
  1. static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs)  
  2. {  
  3.     int retval;  
  4.     retval = kobject_set_name_vargs(kobj, fmt, vargs);        //設置kobject的名稱  
  5.     if (retval) {  
  6.         printk(KERN_ERR "kobject: can not set name properly!\n");  
  7.         return retval;  
  8.     }  
  9.     kobj->parent = parent;            //設置kobject的父kobject  
  10.     return kobject_add_internal(kobj);    //添加kobject  
  11. }  
  1. static int kobject_add_internal(struct kobject *kobj)  
  2.   
  3.     {  
  4.         int error = 0;  
  5.         struct kobject *parent;  
  6.         if (!kobj)        //檢查是否爲空  
  7.             return -ENOENT;  
  8.         if (!kobj->name || !kobj->name[0]) {    //kobj是否有名稱,如果沒有則不能註冊,生成目錄。  
  9.             WARN(1, "kobject: (%p): attempted to be registered with empty "  
  10.                  "name!\n", kobj);  
  11.             return -EINVAL;  
  12.         }  
  13.         parent = kobject_get(kobj->parent);    //獲得父kobject,並增加父kobject的引用計數  
  14.         /* join kset if set, use it as parent if we do not already have one */  
  15.         if (kobj->kset) {                                        //是否有kset集合  
  16.             if (!parent)                                            //如果沒有父kobject則用kset中的kobject對象  
  17.                 parent = kobject_get(&kobj->kset->kobj);  
  18.             kobj_kset_join(kobj);            //將kobject添加進它關聯的kset的list鏈表中。  
  19.             kobj->parent = parent;        //設置父koject  
  20.         }  
  21.         pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",  
  22.              kobject_name(kobj), kobj, __func__,  
  23.              parent ? kobject_name(parent) : "<NULL>",  
  24.              kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");  
  25.         error = create_dir(kobj);                //創建kobject的相應目錄  
  26.           
  27.         if (error) {        //創建時出錯處理  
  28.             kobj_kset_leave(kobj);  
  29.             kobject_put(parent);  
  30.             kobj->parent = NULL;  
  31.             /* be noisy on error issues */  
  32.             if (error == -EEXIST)  
  33.                 printk(KERN_ERR "%s failed for %s with "  
  34.                  "-EEXIST, don't try to register things with "  
  35.                  "the same name in the same directory.\n",  
  36.                  __func__, kobject_name(kobj));  
  37.             else  
  38.                 printk(KERN_ERR "%s failed for %s (%d)\n",  
  39.                  __func__, kobject_name(kobj), error);  
  40.             dump_stack();  
  41.         } else  
  42.             kobj->state_in_sysfs = 1;        //標記爲已經註冊進sysfs  
  43.         return error;  
  44.     }  
sysfs創建目錄函數create_dir,在lib/kobject.c
  1. static int create_dir(struct kobject *kobj)  
  2. {  
  3.     int error = 0;  
  4.     if (kobject_name(kobj)) {  
  5.         error = sysfs_create_dir(kobj);            //在sysfs中創建目錄,將來有時間了可以分析下sysfs子系統。  
  6.         if (!error) {  
  7.             error = populate_dir(kobj);  
  8.             if (error)  
  9.                 sysfs_remove_dir(kobj);  
  10.         }  
  11.     }  
  12.     return error;  
  13. }  

以上函數的主要調用關係,如下
kobject_init_and_add
kobject_init
kobject_add_varg
kobject_add_internal
create_dir
還有一個函數kobject_add,也可以添加一個kobject,它只是沒有kobject_init這一步。
  1. int kobject_add(struct kobject *kobj, struct kobject *parent,  
  2.         const char *fmt, ...)  
  3. {  
  4.     va_list args;  
  5.     int retval;  
  6.     if (!kobj)  
  7.         return -EINVAL;  
  8.     if (!kobj->state_initialized) {  
  9.         printk(KERN_ERR "kobject '%s' (%p): tried to add an "  
  10.          "uninitialized object, something is seriously wrong.\n",  
  11.          kobject_name(kobj), kobj);  
  12.         dump_stack();  
  13.         return -EINVAL;  
  14.     }  
  15.     va_start(args, fmt);  
  16.     retval = kobject_add_varg(kobj, parent, fmt, args);  
  17.     va_end(args);  
  18.     return retval;  


發佈了11 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章