Linux內(nèi)核大量使用面向?qū)ο蟮脑O(shè)計(jì)思想,通過(guò)追蹤源碼,我們甚至可以使用面向?qū)ο笳Z(yǔ)言常用的UML類圖來(lái)分析Linux設(shè)備管理的"類"之間的關(guān)系。這里以4.8.5內(nèi)核為例從kobject,kset,ktype的分析入手,進(jìn)而一探內(nèi)核對(duì)于設(shè)備的管理方式
container_of宏
這個(gè)宏幾乎是linux數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ),Linux中的鏈表與傳統(tǒng)的鏈表不同,其鏈表的節(jié)點(diǎn)本身并不包含任何數(shù)據(jù),任何想要插入到鏈表的數(shù)據(jù)只需要包含一個(gè)事先寫(xiě)好的節(jié)點(diǎn)
//include/linux/types.h184 struct list_head { 185 struct list_head *next, *prev;186 };
但是,使用這種通用的鏈表的第一個(gè)問(wèn)題就是如何根據(jù)一個(gè)list_head成員來(lái)找到相應(yīng)的數(shù)據(jù),Linux社區(qū)的大神們?cè)缇驼业搅讼鄳?yīng)的方法,就是利用下面這個(gè)container_of宏,只需要輸入成員指針ptr,包含該成員的結(jié)構(gòu)體類型type,以及該成員在結(jié)構(gòu)體中名字name就可以返回包含ptr的type類型的結(jié)構(gòu)首地址,這個(gè)宏充分利用了C語(yǔ)言直接操作內(nèi)存的特性。需要注意的是,如果單純?yōu)榱说玫降刂分恍枰猵tr-&((type* 0)->member),內(nèi)核的寫(xiě)法其實(shí)還利用了編譯器的類型檢查機(jī)制做了一份校驗(yàn)工作,即如果傳入的ptr類型和type->member的類型不匹配,會(huì)報(bào)錯(cuò),
//include/linux/kernel.h14 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 830 #define container_of(ptr, type, member) ({ \ 831 const typeof( ((type *)0)->member ) *__mptr = (ptr); \832 (type *)( (char *)__mptr - offsetof(type,member) );})
kobject結(jié)構(gòu)
Linux內(nèi)核中有大量的驅(qū)動(dòng),而這些驅(qū)動(dòng)往往具有類似的結(jié)構(gòu),根據(jù)面向?qū)ο蟮乃枷耄覀兙涂梢詫⑦@些共同的部分提取為父類,這個(gè)父類就是kobject,也就是驅(qū)動(dòng)編程中使用的.ko文件的由來(lái),下面這張圖是我根據(jù)內(nèi)核源碼的kobject繪制的簡(jiǎn)單的UML圖,從中可以看出,kobject包含了大量的設(shè)備必須的信息,而三大類設(shè)備驅(qū)動(dòng)都需要包含這個(gè)kobject結(jié)構(gòu),也就是"繼承"自kobject。一個(gè)kobject對(duì)象就對(duì)應(yīng)sys目錄中的一個(gè)設(shè)備。
內(nèi)核源碼中的kobject結(jié)構(gòu)定義如下
//include/linux/kobject.h 63 struct kobject { 64 const char *name; 65 struct list_head entry; 66 struct kobject *parent; 67 struct kset *kset; 68 struct kobj_type *ktype; 69 struct kernfs_node *sd; /* sysfs directory entry */ 70 struct kref kref; 71 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE 72 struct delayed_work release; 73 #endif 74 unsigned int state_initialized:1; 75 unsigned int state_in_sysfs:1; 76 unsigned int state_add_uevent_sent:1; 77 unsigned int state_remove_uevent_sent:1; 78 unsigned int uevent_suppress:1; 79 };
這個(gè)結(jié)構(gòu)中,
struct kobject
--64-->name表示kobject對(duì)象的名字,對(duì)應(yīng)sysfs下的一個(gè)目錄。
--65-->entry是kobject中插入的head_list結(jié)構(gòu),
--66-->parent是指向當(dāng)前kobject父對(duì)象的指針,體現(xiàn)在sys結(jié)構(gòu)中就是包含當(dāng)前kobject對(duì)象的目錄對(duì)象,
--67-->kset表示當(dāng)前kobject對(duì)象所屬的集合,
--68-->ktype表示當(dāng)前kobject的類型。
--69-->sd用于表示VFS文件系統(tǒng)的目錄項(xiàng),是設(shè)備與文件之間的橋梁,sysfs中的符號(hào)鏈接就是通過(guò)kernfs_node內(nèi)的聯(lián)合體實(shí)現(xiàn)的。
--70-->kref是對(duì)kobject的引用計(jì)數(shù),當(dāng)引用計(jì)數(shù)為0時(shí),就回調(diào)之前注冊(cè)的release方法釋放該對(duì)象。
--74-->state_initialized:1初始化標(biāo)志位,在對(duì)象初始化時(shí)被置位,表示對(duì)象是否已經(jīng)被初始化。
--75-->state_in_sysfs:1表示kobject對(duì)象在sysfs中的狀態(tài),在對(duì)應(yīng)目錄中被創(chuàng)建則置1,否則為0。
--76-->state_add_uevent_sent:1是添加設(shè)備的uevent事件是否發(fā)送標(biāo)志,添加設(shè)備時(shí)會(huì)向用戶空間發(fā)送uevent事件,請(qǐng)求新增設(shè)備。
--77-->state_remove_uevent_sent:1是刪除設(shè)備的uevent事件是否發(fā)送標(biāo)志,刪除設(shè)備時(shí)會(huì)向用戶空間發(fā)送uevent事件,請(qǐng)求卸載設(shè)備
kobject操作
4.8.5的內(nèi)核在lib/koject.c等源碼中定義了一系列對(duì)kobject操作的函數(shù),這里只列出最簡(jiǎn)單的幾個(gè)
初始化kobject
187 static void kobject_init_internal(struct kobject *kobj) 188 { 189 if (!kobj) 190 return; 191 kref_init(&kobj->kref); 192 INIT_LIST_HEAD(&kobj->entry); 193 kobj->state_in_sysfs = 0; 194 kobj->state_add_uevent_sent = 0; 195 kobj->state_remove_uevent_sent = 0; 196 kobj->state_initialized = 1; 197 } 325 void kobject_init(struct kobject *kobj, struct kobj_type *ktype) 326 { 327 char *err_str; ... 344 kobject_init_internal(kobj); 345 kobj->ktype = ktype; 346 return; ... 351 }
注冊(cè)kobject
//添加kobject到內(nèi)核 200 static int kobject_add_internal(struct kobject *kobj) 201 { 202 int error = 0; 203 struct kobject *parent; ... 214 parent = kobject_get(kobj->parent); 215 216 /* join kset if set, use it as parent if we do not already have one */ 217 if (kobj->kset) { 218 if (!parent) 219 parent = kobject_get(&kobj->kset->kobj); 220 kobj_kset_join(kobj); 221 kobj->parent = parent; 222 } 223 224 pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n", 225 kobject_name(kobj), kobj, __func__, 226 parent ? kobject_name(parent) : "", 227 kobj->kset ? kobject_name(&kobj->kset->kobj) : ""); 228 229 error = create_dir(kobj); ... 246 kobj->state_in_sysfs = 1; 247 248 return error; 249 } 354 static __printf(3, 0) int kobject_add_varg(struct kobject *kobj, 355 struct kobject *parent, 356 const char *fmt, va_list vargs) 357 { 358 int retval; ... 365 kobj->parent = parent; 366 return kobject_add_internal(kobj); 367 } 394 int kobject_add(struct kobject *kobj, struct kobject *parent, 395 const char *fmt, ...) 396 { 397 va_list args; 398 int retval; ... 410 va_start(args, fmt); 411 retval = kobject_add_varg(kobj, parent, fmt, args); 412 va_end(args); 413 414 return retval; 415 }
初始化并注冊(cè)kobject
429 int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, 430 struct kobject *parent, const char *fmt, ...) 431 { 432 va_list args; 433 int retval; 434 435 kobject_init(kobj, ktype); 436 437 va_start(args, fmt); 438 retval = kobject_add_varg(kobj, parent, fmt, args); 439 va_end(args); 440 441 return retval; 442 }
注銷kobject
569 void kobject_del(struct kobject *kobj) 570 { 571 struct kernfs_node *sd; 572 573 if (!kobj) 574 return; 575 576 sd = kobj->sd; 577 sysfs_remove_dir(kobj); 578 sysfs_put(sd); 579 580 kobj->state_in_sysfs = 0; 581 kobj_kset_leave(kobj); 582 kobject_put(kobj->parent); 583 kobj->parent = NULL; 584 }
kobject計(jì)數(shù)加一
//lib/kobject.c 591 struct kobject *kobject_get(struct kobject *kobj) 592 { 593 if (kobj) { 594 if (!kobj->state_initialized) 595 WARN(1, KERN_WARNING "kobject: '%s' (%p): is not " 596 "initialized, yet kobject_get() is being " 597 "called.\n", kobject_name(kobj), kobj); 598 kref_get(&kobj->kref); 599 } 600 return kobj; 601 }
kobject計(jì)數(shù)減一
//將kobject對(duì)象的引用計(jì)數(shù)加1,同時(shí)返回該對(duì)象指針。//include/linux/kref.h 40 static inline void kref_get(struct kref *kref) 41 { 42 /* If refcount was 0 before incrementing then we have a race 43 * condition when this kref is freeing by some other thread right now. 44 * In this case one should use kref_get_unless_zero() 45 */ 46 WARN_ON_ONCE(atomic_inc_return(&kref->refcount) < 2); 47 } //lib/kobject.c 591 struct kobject *kobject_get(struct kobject *kobj) 592 { 593 if (kobj) { ... 598 kref_get(&kobj->kref); 599 } 600 return kobj; 601 }
//將kobject對(duì)象的引用計(jì)數(shù)加1,如果減為零就釋放//include/linux/kref.h 67 static inline int kref_sub(struct kref *kref, unsigned int count, 68 void (*release)(struct kref *kref)) 69 { 70 WARN_ON(release == NULL); 71 72 if (atomic_sub_and_test((int) count, &kref->refcount)) { 73 release(kref); 74 return 1; 75 } 76 return 0; 77 } 96 static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref)) 97 { 98 return kref_sub(kref, 1, release); 99 }//lib/kobject.c 684 void kobject_put(struct kobject *kobj) 685 { 686 if (kobj) { ... 691 kref_put(&kobj->kref, kobject_release); 692 } 693 }
kset結(jié)構(gòu)
kset表示一組kobject的集合,kobject通過(guò)kset組織成層次化的結(jié)構(gòu),所有屬于該kset的kobjetc結(jié)構(gòu)的parent指針指向kset包含的kobject對(duì)象,構(gòu)成一個(gè)父子層次關(guān)系這些kobject可以是不同或相同的類型(kobj_type)。sysfs中的設(shè)備組織結(jié)構(gòu)很大程度上都是根據(jù)kset進(jìn)行組織的,比如"/sys/drivers"目錄就是一個(gè)kset對(duì)象,包含系統(tǒng)中的驅(qū)動(dòng)程序?qū)?yīng)的目錄,驅(qū)動(dòng)程序的目錄又kobject表示。比如在平臺(tái)設(shè)備模型中,當(dāng)我們注冊(cè)一個(gè)設(shè)備或驅(qū)動(dòng)到平臺(tái)總線,其實(shí)是將對(duì)應(yīng)的kobject掛接到platform總線的kset上,每種總線都是維護(hù)兩條鏈表(兩個(gè)kset),一條用于鏈接掛接在上面的驅(qū)動(dòng)(驅(qū)動(dòng)kset),一條用于鏈接掛接在上面的設(shè)備(設(shè)備kset)。
//include/linux/kobject.h168 struct kset {169 struct list_head list;170 spinlock_t list_lock;171 struct kobject kobj;172 const struct kset_uevent_ops*uevent_ops; 173 };
下面簡(jiǎn)單分析一下其中的成員
struct kset
--169-->list_head還是那個(gè)用來(lái)掛在鏈表上的結(jié)構(gòu),包含在一個(gè)kset的所有kobject構(gòu)成了一個(gè)雙向循環(huán)鏈表,list_head就是這個(gè)鏈表的頭部,這個(gè)鏈表用來(lái)連接第一個(gè)和最后一個(gè)kobject對(duì)象,第一個(gè)kobjetc使用entry連接kset集合以及第二個(gè)kobject對(duì)象,第二個(gè)kobject對(duì)象使用entry連接第一個(gè)kobject對(duì)象和第三個(gè)kobject對(duì)象,依次類推,最終形成一個(gè)kobject對(duì)象的鏈表
--171-->kobj(171)是歸屬于該kset的所有的kobject的共有parent,這個(gè)parent就是體現(xiàn)內(nèi)核設(shè)備組織結(jié)構(gòu)的關(guān)鍵,同時(shí),kset的引用計(jì)數(shù)就是內(nèi)嵌的kobject對(duì)象的引用次數(shù)。
kset操作
下面是幾個(gè)關(guān)于kset的基礎(chǔ)操作方法
初始化kset
//lib/kobject.c 187 static void kobject_init_internal(struct kobject *kobj) 188 { 189 if (!kobj) 190 return; 191 kref_init(&kobj->kref); 192 INIT_LIST_HEAD(&kobj->entry); 193 kobj->state_in_sysfs = 0; 194 kobj->state_add_uevent_sent = 0; 195 kobj->state_remove_uevent_sent = 0; 196 kobj->state_initialized = 1; 197 } 767 void kset_init(struct kset *k) 768 { 769 kobject_init_internal(&k->kobj); 770 INIT_LIST_HEAD(&k->list); 771 spin_lock_init(&k->list_lock); 772 }
注冊(cè)kset
//lib/kobject.c 809 int kset_register(struct kset *k) 810 { 811 int err; 812 813 if (!k) 814 return -EINVAL; 815 816 kset_init(k); 817 err = kobject_add_internal(&k->kobj); 818 if (err) 819 return err; 820 kobject_uevent(&k->kobj, KOBJ_ADD); 821 return 0; 822 }
注銷kset
//lib/kobject.c 829 void kset_unregister(struct kset *k) 830 { 831 if (!k) 832 return; 833 kobject_del(&k->kobj); 834 kobject_put(&k->kobj); 835 }
kset計(jì)數(shù)加一
//include/linux/kobject.h187 static inline struct kset *kset_get(struct kset *k) 188 {189 return k ? to_kset(kobject_get(&k->kobj)) : NULL;190 }
kset計(jì)數(shù)減一
192 static inline void kset_put(struct kset *k)193 {194 kobject_put(&k->kobj);195 }
kobj_type結(jié)構(gòu)
//include/linux/kobject.h116 struct kobj_type {117 void (*release)(struct kobject *kobj);118 const struct sysfs_ops *sysfs_ops;119 struct attribute **default_attrs; 120 const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);121 const void *(*namespace)(struct kobject *kobj);122 };
這個(gè)結(jié)構(gòu)主要是表征kobject的類型,
struct ktype
--117-->release是一個(gè)釋放kobject對(duì)象的接口,有點(diǎn)像面向?qū)ο笾械奈鰳?gòu)。
--118-->sysfs_ops是操作kobject的方法集,
//include/linux/sysfs.h 29 struct attribute { 30 const char *name; 31 umode_t mode; 32 #ifdef CONFIG_DEBUG_LOCK_ALLOC 33 bool ignore_lockdep:1; 34 struct lock_class_key *key; 35 struct lock_class_key skey; 36 #endif 37 }; 209 struct sysfs_ops { 210 ssize_t (*show)(struct kobject *, struct attribute *, char *);211 ssize_t (*store)(struct kobject *, struct attribute *, const char *, si212 };
struct sysfs_ops
--210-->我們?cè)谑褂胏at echo等工具(read()/write()系統(tǒng)調(diào)用)進(jìn)行讀寫(xiě)sysfs中相應(yīng)驅(qū)動(dòng)的屬性時(shí),其實(shí)就是回調(diào)驅(qū)動(dòng)的show()和store()。由此可見(jiàn),對(duì)同一類型的kobject操作會(huì)回調(diào)同一個(gè)kobj_type的方法
//include/linux/kobject.h197 static inline struct kobj_type *get_ktype(struct kobject *kobj) 198 {199 return kobj->ktype;200 }
從這個(gè)函數(shù)中可以看出,4.8.5提取kobject的kobj_type的時(shí)候直接提取kobject的,我還測(cè)試過(guò)3.14版本的,也是這種寫(xiě)法,不過(guò)網(wǎng)上還有下面的這種get_ktype的實(shí)現(xiàn),還沒(méi)找到具體是哪個(gè)版本,顯然,這個(gè)版本中kset中的ktype這個(gè)類型優(yōu)先于 kobject 自身中的 ktype 。因此在典型的應(yīng)用中, 在 struct kobject 中的 ktype 成員被設(shè)為 NULL, 而 kset 中的ktype是實(shí)際被使用的。
static inline struct kobj_type * get_ktype(struct kobject * k) { if (k->kset && k->kset->ktype) return k->kset->ktype; else return k->ktype; }
結(jié)構(gòu)框圖
kobject,kset是Linux設(shè)備管理中的基本結(jié)構(gòu)體,但在實(shí)際操作中我們幾乎不會(huì)實(shí)際操作這些結(jié)構(gòu),因?yàn)樗麄儽旧聿⒉痪哂嗅槍?duì)某一個(gè)具體設(shè)備或驅(qū)動(dòng)的信息,在Linux內(nèi)核中,這兩個(gè)結(jié)構(gòu)都是被包含具體的設(shè)備結(jié)構(gòu)中,比如cdev,gendisk等,從面向?qū)ο蟮慕嵌瓤紤],就是每一類設(shè)備都可以看作這兩個(gè)結(jié)構(gòu)的子類。
通過(guò)上面的分析,我們可以看出這三者之間的關(guān)系,并畫(huà)出下面的結(jié)構(gòu)框圖,sysfs中的上目錄結(jié)構(gòu)就是根據(jù)kset之間的數(shù)據(jù)組織方式進(jìn)行呈現(xiàn)的。
?
評(píng)論
查看更多