Linux設(shè)備管理(一)_kobject,、kset、ktype分析(超詳細(xì))
Linux內(nèi)核大量使用面向?qū)ο蟮脑O(shè)計(jì)思想,通過追蹤源碼,我們甚至可以使用面向?qū)ο笳Z言常用的UML類圖來分析Linux設(shè)備管理的"類"之間的關(guān)系。這里以4.8.5內(nèi)核為例從kobject,kset,ktype的分析入手,進(jìn)而一探內(nèi)核對于設(shè)備的管理方式

container_of宏
這個(gè)宏幾乎是linux數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ),Linux中的鏈表與傳統(tǒng)的鏈表不同,其鏈表的節(jié)點(diǎn)本身并不包含任何數(shù)據(jù),任何想要插入到鏈表的數(shù)據(jù)只需要包含一個(gè)事先寫好的節(jié)點(diǎn)

但是,使用這種通用的鏈表的第一個(gè)問題就是如何根據(jù)一個(gè)list_head成員來找到相應(yīng)的數(shù)據(jù),Linux社區(qū)的大神們早就找到了相應(yīng)的方法,就是利用下面這個(gè)container_of宏,只需要輸入成員指針ptr,包含該成員的結(jié)構(gòu)體類型type,以及該成員在結(jié)構(gòu)體中名字name就可以返回包含ptr的type類型的結(jié)構(gòu)首地址,這個(gè)宏充分利用了C語言直接操作內(nèi)存的特性。需要注意的是,如果單純?yōu)榱说玫降刂分恍枰猵tr-&((type* 0)->member),內(nèi)核的寫法其實(shí)還利用了編譯器的類型檢查機(jī)制做了一份校驗(yàn)工作,即如果傳入的ptr類型和type->member的類型不匹配,會(huì)報(bào)錯(cuò),
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個(gè)人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦!??!前100名進(jìn)群領(lǐng)取,額外贈(zèng)送一份價(jià)值699的內(nèi)核資料包(含視頻教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)? ??


kobject結(jié)構(gòu)
Linux內(nèi)核中有大量的驅(qū)動(dòng),而這些驅(qū)動(dòng)往往具有類似的結(jié)構(gòu),根據(jù)面向?qū)ο蟮乃枷?,我們就可以將這些共同的部分提取為父類,這個(gè)父類就是kobject,也就是驅(qū)動(dòng)編程中使用的.ko文件的由來,下面這張圖是我根據(jù)內(nèi)核源碼的kobject繪制的簡單的UML圖,從中可以看出,kobject包含了大量的設(shè)備必須的信息,而三大類設(shè)備驅(qū)動(dòng)都需要包含這個(gè)kobject結(jié)構(gòu),也就是"繼承"自kobject。一個(gè)kobject對象就對應(yīng)sys目錄中的一個(gè)設(shè)備。 內(nèi)核源碼中的kobject結(jié)構(gòu)定義如下
這個(gè)結(jié)構(gòu)中,
struct kobject?
--64-->name表示kobject對象的名字,對應(yīng)sysfs下的一個(gè)目錄。?
--65-->entry是kobject中插入的head_list結(jié)構(gòu),?
--66-->parent是指向當(dāng)前kobject父對象的指針,體現(xiàn)在sys結(jié)構(gòu)中就是包含當(dāng)前kobject對象的目錄對象,?
--67-->kset表示當(dāng)前kobject對象所屬的集合,?
--68-->ktype表示當(dāng)前kobject的類型。?
--69-->sd用于表示VFS文件系統(tǒng)的目錄項(xiàng),是設(shè)備與文件之間的橋梁,sysfs中的符號(hào)鏈接就是通過kernfs_node內(nèi)的聯(lián)合體實(shí)現(xiàn)的。 -
-70-->kref是對kobject的引用計(jì)數(shù),當(dāng)引用計(jì)數(shù)為0時(shí),就回調(diào)之前注冊的release方法釋放該對象。?
--74-->state_initialized:1初始化標(biāo)志位,在對象初始化時(shí)被置位,表示對象是否已經(jīng)被初始化。?
--75-->state_in_sysfs:1表示kobject對象在sysfs中的狀態(tài),在對應(yīng)目錄中被創(chuàng)建則置1,否則為0。?
--76-->state_add_uevent_sent:1是添加設(shè)備的uevent事件是否發(fā)送標(biāo)志,添加設(shè)備時(shí)會(huì)向用戶空間發(fā)送uevent事件,請求新增設(shè)備。?
--77-->state_remove_uevent_sent:1是刪除設(shè)備的uevent事件是否發(fā)送標(biāo)志,刪除設(shè)備時(shí)會(huì)向用戶空間發(fā)送uevent事件,請求卸載設(shè)備
kobject操作
4.8.5的內(nèi)核在lib/koject.c等源碼中定義了一系列對kobject操作的函數(shù),這里只列出最簡單的幾個(gè)
初始化kobject
注冊kobject
初始化并注冊kobject
注銷kobject
kobject計(jì)數(shù)加一
kobject計(jì)數(shù)減一
kset結(jié)構(gòu)
kset表示一組kobject的集合,kobject通過kset組織成層次化的結(jié)構(gòu),所有屬于該kset的kobjetc結(jié)構(gòu)的parent指針指向kset包含的kobject對象,構(gòu)成一個(gè)父子層次關(guān)系這些kobject可以是不同或相同的類型(kobj_type)。sysfs中的設(shè)備組織結(jié)構(gòu)很大程度上都是根據(jù)kset進(jìn)行組織的,比如"/sys/drivers"目錄就是一個(gè)kset對象,包含系統(tǒng)中的驅(qū)動(dòng)程序?qū)?yīng)的目錄,驅(qū)動(dòng)程序的目錄又kobject表示。比如在平臺(tái)設(shè)備模型中,當(dāng)我們注冊一個(gè)設(shè)備或驅(qū)動(dòng)到平臺(tái)總線,其實(shí)是將對應(yīng)的kobject掛接到platform總線的kset上,每種總線都是維護(hù)兩條鏈表(兩個(gè)kset),一條用于鏈接掛接在上面的驅(qū)動(dòng)(驅(qū)動(dòng)kset),一條用于鏈接掛接在上面的設(shè)備(設(shè)備kset)。
下面簡單分析一下其中的成員
struct kset --169-->list_head還是那個(gè)用來掛在鏈表上的結(jié)構(gòu),包含在一個(gè)kset的所有kobject構(gòu)成了一個(gè)雙向循環(huán)鏈表,list_head就是這個(gè)鏈表的頭部,這個(gè)鏈表用來連接第一個(gè)和最后一個(gè)kobject對象,第一個(gè)kobjetc使用entry連接kset集合以及第二個(gè)kobject對象,第二個(gè)kobject對象使用entry連接第一個(gè)kobject對象和第三個(gè)kobject對象,依次類推,最終形成一個(gè)kobject對象的鏈表 --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對象的引用次數(shù)。

kset操作
下面是幾個(gè)關(guān)于kset的基礎(chǔ)操作方法
初始化kset
注冊kset
注銷kset
kset計(jì)數(shù)加一
kset計(jì)數(shù)減一
kobj_type結(jié)構(gòu)
這個(gè)結(jié)構(gòu)主要是表征kobject的類型,
struct ktype?
--117-->release是一個(gè)釋放kobject對象的接口,有點(diǎn)像面向?qū)ο笾械奈鰳?gòu)。?
--118-->sysfs_ops是操作kobject的方法集,
struct sysfs_ops --210-->我們在使用cat echo等工具(read()/write()系統(tǒng)調(diào)用)進(jìn)行讀寫sysfs中相應(yīng)驅(qū)動(dòng)的屬性時(shí),其實(shí)就是回調(diào)驅(qū)動(dòng)的show()和store()。由此可見,對同一類型的kobject操作會(huì)回調(diào)同一個(gè)kobj_type的方法
從這個(gè)函數(shù)中可以看出,4.8.5提取kobject的kobj_type的時(shí)候直接提取kobject的,我還測試過3.14版本的,也是這種寫法,不過網(wǎng)上還有下面的這種get_ktype的實(shí)現(xiàn),還沒找到具體是哪個(gè)版本,顯然,這個(gè)版本中kset中的ktype這個(gè)類型優(yōu)先于 kobject 自身中的 ktype 。因此在典型的應(yīng)用中, 在 struct kobject 中的 ktype 成員被設(shè)為 NULL, 而 kset 中的ktype是實(shí)際被使用的。
結(jié)構(gòu)框圖
kobject,kset是Linux設(shè)備管理中的基本結(jié)構(gòu)體,但在實(shí)際操作中我們幾乎不會(huì)實(shí)際操作這些結(jié)構(gòu),因?yàn)樗麄儽旧聿⒉痪哂嗅槍δ骋粋€(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ān)系,并畫出下面的結(jié)構(gòu)框圖,sysfs中的上目錄結(jié)構(gòu)就是根據(jù)kset之間的數(shù)據(jù)組織方式進(jìn)行呈現(xiàn)的。

