Linux內核大量使用面向對象的設計思想,經過追蹤源碼,咱們甚至可使用面嚮對象語言經常使用的UML類圖來分析Linux設備管理的"類"之間的關係。這裏以4.8.5內核爲例從kobject,kset,ktype的分析入手,進而一探內核對於設備的管理方式node
這個宏幾乎是linux數據結構的基礎,Linux中的鏈表與傳統的鏈表不一樣,其鏈表的節點自己並不包含任何數據,任何想要插入到鏈表的數據只須要包含一個事先寫好的節點linux
//include/linux/types.h 184 struct list_head { 185 struct list_head *next, *prev; 186 };
可是,使用這種通用的鏈表的第一個問題就是如何根據一個list_head成員來找到相應的數據,Linux社區的大神們早就找到了相應的方法,就是利用下面這個container_of宏,只須要輸入成員指針ptr,包含該成員的結構體類型type,以及該成員在結構體中名字name就能夠返回包含ptr的type類型的結構首地址,這個宏充分利用了C語言直接操做內存的特性。須要注意的是,若是單純爲了獲得地址只須要ptr-&((type* 0)->member)
,內核的寫法其實還利用了編譯器的類型檢查機制作了一份校驗工做,即若是傳入的ptr類型和type->member的類型不匹配,會報錯,編程
//include/linux/kernel.h 14 #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) );})
Linux內核中有大量的驅動,而這些驅動每每具備相似的結構,根據面向對象的思想,咱們就能夠將這些共同的部分提取爲父類,這個父類就是kobject,也就是驅動編程中使用的.ko文件的由來,下面這張圖是我根據內核源碼的kobject繪製的簡單的UML圖,從中能夠看出,kobject包含了大量的設備必須的信息,而三大類設備驅動都須要包含這個kobject結構,也就是"繼承"自kobject。一個kobject對象就對應sys目錄中的一個設備。
內核源碼中的kobject結構定義以下數據結構
//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 };
這個結構中,less
struct kobject
--64-->name表示kobject對象的名字,對應sysfs下的一個目錄。
--65-->entry是kobject中插入的head_list結構,
--66-->parent是指向當前kobject父對象的指針,體如今sys結構中就是包含當前kobject對象的目錄對象,
--67-->kset表示當前kobject對象所屬的集合,
--68-->ktype表示當前kobject的類型。
--69-->sd用於表示VFS文件系統的目錄項,是設備與文件之間的橋樑,sysfs中的符號連接就是經過kernfs_node內的聯合體實現的。
--70-->kref是對kobject的引用計數,當引用計數爲0時,就回調以前註冊的release方法釋放該對象。
--74-->state_initialized:1初始化標誌位,在對象初始化時被置位,表示對象是否已經被初始化。
--75-->state_in_sysfs:1表示kobject對象在sysfs中的狀態,在對應目錄中被建立則置1,不然爲0。
--76-->state_add_uevent_sent:1是添加設備的uevent事件是否發送標誌,添加設備時會向用戶空間發送uevent事件,請求新增設備。
--77-->state_remove_uevent_sent:1是刪除設備的uevent事件是否發送標誌,刪除設備時會向用戶空間發送uevent事件,請求卸載設備函數
4.8.5的內核在lib/koject.c等源碼中定義了一系列對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 }
//添加kobject到內核 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) : "<NULL>", 227 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>"); 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 }
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 }
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 }
//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對象的引用計數加1,同時返回該對象指針。 //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對象的引用計數加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表示一組kobject的集合,kobject經過kset組織成層次化的結構,全部屬於該kset的kobjetc結構的parent指針指向kset包含的kobject對象,構成一個父子層次關係這些kobject能夠是不一樣或相同的類型(kobj_type)。sysfs中的設備組織結構很大程度上都是根據kset進行組織的,好比"/sys/drivers"目錄就是一個kset對象,包含系統中的驅動程序對應的目錄,驅動程序的目錄又kobject表示。好比在平臺設備模型中,當咱們註冊一個設備或驅動到平臺總線,實際上是將對應的kobject掛接到platform總線的kset上,每種總線都是維護兩條鏈表(兩個kset),一條用於連接掛接在上面的驅動(驅動kset),一條用於連接掛接在上面的設備(設備kset)。測試
//include/linux/kobject.h 168 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 };
下面簡單分析一下其中的成員this
struct kset
--169-->list_head仍是那個用來掛在鏈表上的結構,包含在一個kset的全部kobject構成了一個雙向循環鏈表,list_head就是這個鏈表的頭部,這個鏈表用來鏈接第一個和最後一個kobject對象,第一個kobjetc使用entry鏈接kset集合以及第二個kobject對象,第二個kobject對象使用entry鏈接第一個kobject對象和第三個kobject對象,依次類推,最終造成一個kobject對象的鏈表
--171-->kobj(171)是歸屬於該kset的全部的kobject的共有parent,這個parent就是體現內核設備組織結構的關鍵,同時,kset的引用計數就是內嵌的kobject對象的引用次數。atom
下面是幾個關於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 }
//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 }
//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 }
//include/linux/kobject.h 187 static inline struct kset *kset_get(struct kset *k) 188 { 189 return k ? to_kset(kobject_get(&k->kobj)) : NULL; 190 }
192 static inline void kset_put(struct kset *k) 193 { 194 kobject_put(&k->kobj); 195 }
//include/linux/kobject.h 116 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 };
這個結構主要是表徵kobject的類型,
struct ktype
--117-->release是一個釋放kobject對象的接口,有點像面向對象中的析構。
--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 *, si 212 };
struct sysfs_ops
--210-->咱們在使用cat echo等工具(read()/write()系統調用)進行讀寫sysfs中相應驅動的屬性時,其實就是回調驅動的show()和store()。因而可知,對同一類型的kobject操做會回調同一個kobj_type的方法
//include/linux/kobject.h 197 static inline struct kobj_type *get_ktype(struct kobject *kobj) 198 { 199 return kobj->ktype; 200 }
從這個函數中能夠看出,4.8.5提取kobject的kobj_type的時候直接提取kobject的,我還測試過3.14版本的,也是這種寫法,不過網上還有下面的這種get_ktype的實現,還沒找到具體是哪一個版本,顯然,這個版本中kset中的ktype這個類型優先於 kobject 自身中的 ktype 。所以在典型的應用中, 在 struct kobject 中的 ktype 成員被設爲 NULL, 而 kset 中的ktype是實際被使用的。
static inline struct kobj_type * get_ktype(struct kobject * k) { if (k->kset && k->kset->ktype) return k->kset->ktype; else return k->ktype; }
kobject,kset是Linux設備管理中的基本結構體,但在實際操做中咱們幾乎不會實際操做這些結構,由於他們自己並不具備針對某一個具體設備或驅動的信息,在Linux內核中,這兩個結構都是被包含具體的設備結構中,好比cdev,gendisk等,從面向對象的角度考慮,就是每一類設備均可以看做這兩個結構的子類。
經過上面的分析,咱們能夠看出這三者之間的關係,並畫出下面的結構框圖,sysfs中的上目錄結構就是根據kset之間的數據組織方式進行呈現的。