Kobject模型之linux內核源碼

一。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: pointerto the kobjectto initialize
  4.  * @ktype: pointerto the ktypefor this kobject.
  5.  *
  6.  * This function will properly initialize a kobject such that it canthen
  7.  * be passed to the kobject_add()call.
  8.  *
  9.  * After this function is called, the kobject MUST be cleaned up by acall
  10.  * to kobject_put(),not by acallto kfree directlyto 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.         /*donot 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. }
  1. void kref_get(struct kref *kref)
  2. {
  3.     WARN_ON(!atomic_read(&kref->refcount));
  4.     atomic_inc(&kref->refcount);    //將kref中的這個原子變量加1
  5.     smp_mb__after_atomic_inc();
  6. }
減少一個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, belongingto a specific subsystem.
  3.  *
  4.  * A kset defines a group of kobjects. They can be individually
  5.  * different "types" but overall these kobjects all wantto be grouped
  6.  * together and operatedonin the same manner. ksets are usedto
  7.  * define the attribute callbacks and other common events that happen to
  8.  * a kobject.
  9.  *
  10.  * @list: the list of all kobjectsfor this kset
  11.  * @list_lock: a lockfor iterating over the kobjects
  12.  * @kobj: the embedded kobjectfor this kset(recursion, isn't it fun...)
  13.  * @uevent_ops: theset of uevent operationsfor this kset. These are
  14.  * called whenever a kobject has something happento it so that the kset
  15.  * can add new environment variables,orfilter out the ueventsif 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 ksetfor 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 itto sysfs
  3.  *
  4.  * @name: the namefor the kset
  5.  * @uevent_ops: a struct kset_uevent_opsfor the kset
  6.  * @parent_kobj: the parent kobject of this kset,if any.
  7.  *
  8.  * This function creates a kset structure dynamicallyand 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 wasnot ableto 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.  * kset_create - create a struct kset dynamically
  3.  *
  4.  * @name: the namefor the kset
  5.  * @uevent_ops: a struct kset_uevent_opsfor 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 systemand show upin sysfs with acallto
  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 itis no longer being used.
  13.  *
  14.  * If the kset wasnot ableto be created,NULL will be returned.
  15.  */
  16. static struct kset *kset_create(const char*name,
  17.                 struct kset_uevent_ops *uevent_ops,
  18.                 struct kobject *parent_kobj)
  19. {
  20.     struct kset *kset;
  21.     int retval;

  22.     kset = kzalloc(sizeof(*kset), GFP_KERNEL);
  23.     if (!kset)
  24.         return NULL;
  25.     retval = kobject_set_name(&kset->kobj, name);    //設置kobject名稱
  26.     if (retval){
  27.         kfree(kset);
  28.         return NULL;
  29.     }
  30.     kset->uevent_ops= uevent_ops;
  31.     kset->kobj.parent= parent_kobj;            //設置kset的kobject的父對象

  32.     /*
  33.      * The kobject of this kset will have a type of kset_ktypeand belongto
  34.      * no kset itself. That way we can properly free it when itis
  35.      * finished being used.
  36.      */
  37.     kset->kobj.ktype=&kset_ktype;    //設置kset的kobject的默認屬性
  38.     kset->kobj.kset=NULL;

  39.     return kset;
  40. }
上面這些函數主要包含即調用關係如下,
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 itto the kobject hierarchy
  3.  * @kobj: pointerto the kobjectto initialize
  4.  * @ktype: pointerto the ktypefor this kobject.
  5.  * @parent: pointerto the parent of this kobject.
  6.  * @fmt: the name of the kobject.
  7.  *
  8.  * This function combines thecallto kobject_init()and
  9.  * kobject_add(). The same type oferror handling after acall 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.     int error= 0;
  4.     struct kobject *parent;

  5.     if (!kobj)        //檢查是否爲空
  6.         return -ENOENT;

  7.     if (!kobj->name||!kobj->name[0]){    //kobj是否有名稱,如果沒有則不能註冊,生成目錄。
  8.         WARN(1,"kobject: (%p): attempted to be registered with empty "
  9.              "name!\n", kobj);
  10.         return -EINVAL;
  11.     }

  12.     parent = kobject_get(kobj->parent);    //獲得父kobject,並增加父kobject的引用計數

  13.     /* join kset if set, use it as parent if we do not already have one*/
  14.     if (kobj->kset){                                        //是否有kset集合
  15.         if (!parent)                                            //如果沒有父kobject則用kset中的kobject對象
  16.             parent = kobject_get(&kobj->kset->kobj);
  17.         kobj_kset_join(kobj);            //將kobject添加進它關聯的kset的list鏈表中。
  18.         kobj->parent= parent;        //設置父koject
  19.     }

  20.     pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
  21.          kobject_name(kobj), kobj, __func__,
  22.          parent ? kobject_name(parent):"<NULL>",
  23.          kobj->kset? kobject_name(&kobj->kset->kobj):"<NULL>");

  24.     error = create_dir(kobj);                //創建kobject的相應目錄
  25.     
  26.     if (error){        //創建時出錯處理
  27.         kobj_kset_leave(kobj);
  28.         kobject_put(parent);
  29.         kobj->parent=NULL;

  30.         /* be noisyonerror issues*/
  31.         if (error==-EEXIST)
  32.             printk(KERN_ERR "%s failed for %s with "
  33.              "-EEXIST, don't try to register things with "
  34.              "the same name in the same directory.\n",
  35.              __func__, kobject_name(kobj));
  36.         else
  37.             printk(KERN_ERR "%s failed for %s (%d)\n",
  38.              __func__, kobject_name(kobj),error);
  39.         dump_stack();
  40.     } else
  41.         kobj->state_in_sysfs= 1;        //標記爲已經註冊進sysfs

  42.     return error;
  43. }
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;
  19. }

轉自http://blog.chinaunix.net/uid-11319766-id-3253414.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章