前言
在上一篇中,我們大致描述了LINUX設備模型,我們先來總結一下三要素的關系。
?
?
從圖中可以看出,Linux設備模型就是"總線、設備、驅動、類"這四個概念之前的相互關系;這也是Linux2.6內核抽象出來的用于管理系統中所有設備的模型圖;
簡單地描述設備模型的層次關系如下:
1、驅動核心中可以注冊多種類型的總線(bus_type);
2、每一種類型的總線下面可以掛載許多設備(device);
3、每一種類型的總線可以使用很多設備驅動(device_driver);
4、每一個驅動程序可以管理一組設備;
這種基本關系的建立源于實際系統中各種總線、設備、驅動、類結構的抽象;
?
設備模型之kobject、kset
?
kobject是設備模型中一個很基本的概念,最初支持為了支持引用計數,但是隨著時間的轉移,它承擔了越來越多的任務:
1)對象的引用計數
2)SYS表述
3)數據結構關聯
4)熱插拔處理
?
?
它的定義如下:
struct kobject {
//名稱
?
?
//指向kobject的父對象,以此來在內核中構造一個對象層次結構,并且可以將多個對象之間的關系表現初來,這就是sysfs的真相:一個用戶空間的文件系統,用來表示內核中kobject對象的層次結構。
?
?
?
?
?
?
?
?
?
?
};
一個kobject存在的意義在于把高級對象連接到設備模型上。kobject正如最頂層的基類,而其他類則是派生物,它實現了一系列方法,對自身并沒有額數的作用,但是對其他對象卻非常有效。
這句話可以這么理解,在LINUX中,是用C語言實現的,沒有對象的概念,但是kobject被包含在各個結構中,就如同面向對象的的基類,根據它描述的父子兄弟關系,把各個結構實例聯系起來,想成目錄結構。現在回過頭去看看總線,設備,驅動的結構定義中其實都有這個KOBJECT的影子,只是在前面我們分析的時候沒有把列出來。
kobject的初始化:
1首先使用memest函數將整個kobject清零。
2調用kobject_init()函數。設置結構內部的一些成員。void kobject_init(struct kobject*kobj);kobject_init設置kobject的引用計數為 1。
3設置kobject的名字,這是sysfs入口中使用的名字。int kobject_set_name(struct kobject*kobj, const char *format, ...)
kobject的引用計數:
kobject 的其中一個關鍵函數是作為一個引用計數器, 給一個它被嵌入的對象. 只要對這個對象的引用存在, 這個對象(和支持它的代碼)必須繼續存在.
struct kobject *kobject_get(struct kobject *kobj);
void kobject_put(struct kobject *kobj);
釋放函數和 kobject 類型:
通知由 kobject 的一個釋放函數來完成. 常常地, 這個方法有一個形式如下:
void my_object_release(struct kobject *kobj)
{
?
?
}
有一點需要注意:每一個kobject都必須有一個release方法,并且kobject在該方法被調用前必須保持不變。
而需要在意一點,release函數并沒有包含在kobject自身內,而是與包含kobject的結構類型相關聯的kobj_type數據結構負責對該類型進行跟蹤。
注意結構中的ktype,以及SYSFS_OPS,見下邊SYS相關的分析。
struct kobj_type
{
?
?
?
}
kobject層次結構、kset
內核用kobject結構將各個對象連接起來組成一個分層的結構體系,從而與模型化的子系統相匹配。有兩種獨立的機制用于連接:parent指針和kset
在kobject結構的parent成員中,保存了另外一個kobject結構的指針,這個結構表示了分層結構中上一層的節點,而parent最重要的用途是在sysfs分層結構中定位對象。
kset是嵌入相同類型結構的kobject集合,但是不同之處在于,kobject在乎的是對象的類型,而kset關心的是對象的集合與聚合。需要注意的是,kset總是在sysfs中出現,一旦設置了kset并把它添加到系統中,將在sysfs中創建一個目錄。kobject不必在sysfs中表示,但是kset中的每一個kobject成員都將在sysfs中得到表述。
創建一個對象時,要把一個kobject添加到kset中,要先將kobject的kset成員指向目的的kset,調用extern intkobject_register(struct kobject *kobj);
這個函數僅僅是一個 kobject_init 和 kobject_add 的結合.
struct kset {
?
?
?
?
};
對于初始化和設置, kset有一個接口非常類似于 kobjects.
void kset_init(struct kset *kset);
int kset_add(struct kset *kset);
int kset_register(struct kset *kset);
void kset_unregister(struct kset *kset);
為管理 ksets 的引用計數, 情況大概相同:
struct kset *kset_get(struct kset *kset);
void kset_put(struct kset *kset);
一個 kset 還有一個名子, 存儲于嵌入的 kobject. 因此, 如果你有一個 kset 稱為 my_set,你將設置它的名子用:
kobject_set_name(&my_set->kobj, "Thename");
ksets 還有一個指針( 在 ktye 成員 )指向 kobject_type 結構來描述它包含的 kobject.這個類型優先于在 kobject 自身中的 ktype 成員. 結果, 在典型的應用中, 在 struct kobject 中的ktype 成員被留為 NULL, 因為 kset 中的相同成員是實際使用的那個.
低層sysfs操作
(什么是SYSFS?? 我們就把它先看做設備文件系統,用來管理系統的各種設備,驅動,總線,類的文件系統)。
kobject隱藏在sysfs文件系統之后的機制,對于sysfs每個目錄,內核中都會存在一個對應的kobject。每一個kobject都輸出一個或者多個屬性。在sysfs目錄中表現為文件,其中的內容由內核生成。在<linux/sysfs.h>中包含了sysfs工作代碼。
使kobject在sysfs出現僅僅是調用kobject_add的事情.kobjects的sysfs入口一直為目錄,因此一個對kobject_add的調用導致在sysfs中創建一個目錄.常常地, 這個目錄包含一個或多個屬性;
分配給 kobject 的名子( 用 kobject_set_name ) 是給 sysfs 目錄使用的名子. 因此, 出現在sysfs 層次的相同部分的 kobjects 必須有獨特的名子. 分配給 kobjects 的名子也應當是合理的文件名子:它們不能包含斜線字符, 并且空白的使用強烈不推薦.
sysfs 入口位于對應 kobject 的 parent 指針的目錄中. 如果 parent 是 NULL 當kobject_add 被調用時, 它被設置為嵌在新 kobject 的 kset 中的 kobject;
當被創建時, 每個kobject被給定一套缺省屬性. 這些屬性通過kobj_type結構來指定.
default_attr 成員列舉了對每個這樣類型的 kobject 被創建的屬性, 并且 sysfs_ops提供方法來實現這些屬性.
struct attribute {
?
?
?
};
在這個結構中, name 是屬性的名子( 如同它出現在 kobject 的 sysfs 目錄中), owner是一個指向模塊的指針(如果有一個), 模塊負責這個屬性的實現, 并且 mode 是應用到這個屬性的保護位. mode 常常是S_IRUGO 對于只讀屬性; 如果這個屬性是可寫的, 你可以扔出 S_IWUSR 來只給 root 寫權限( modes 的宏定義在<linux/stat.h> 中). default_attrs列表中的最后一個入口必須用 0 填充.
實現這些屬性則需要kobj_type->sysfs_ops成員, 它指向一個結構, 定義為:
struct sysfs_ops {
?
?
};
無論何時一個屬性從用戶空間讀取, show 方法被用一個指向 kobject 的指針和適當的屬性結構來調用.這個方法應當將給定屬性值編碼進緩沖, 要確定沒有覆蓋它( 它是 PAGE_SIZE 字節), 并且返回實際的被返回數據的長度.sysfs 的慣例表明每個屬性應當包含一個單個的, 人可讀的值; 如果你有許多消息返回, 你可要考慮將它分為多個屬性.
同樣的 show 方法用在所有的和給定 kobject 關聯的屬性. 傳遞到函數的 attr 指針可用來決定需要哪個屬性. 一些show 方法包含對屬性名子的一系列測試. 其他的實現將屬性結構嵌入另一個結構, 來包含需要返回屬性值的信息; 在這種情況下,container_of 可能用在 show 方法中來獲得一個指向嵌入結構的指針.
store 方法類似; 它應當將存在緩沖的數據編碼( size 包含數據的長度, 這不能超過 PAGE_SIZE ),存儲和以任何有意義的的方式響應新數據, 并且返回實際編碼的字節數. store 方法只在屬性的許可允許寫才被調用. 當編寫一個store 方法時, 不要忘記你在接收來自用戶空間的任意信息; 你應當在采取對應動作之前非常小心地驗證它. 如果到數據不匹配期望,返回一個負的錯誤值, 而不是可能地做一些不想要的和無法恢復的事情. 如果你的設備輸出一個自銷毀的屬性,你應當要求一個特定的字符串寫到那里來引發這個功能; 一個偶然的, 隨機寫應當只產生一個錯誤.
非默認屬性:
多數情況下,kobject類型的default_attrs成員描述了kobject擁有的所有屬性。但是我們還可以根據需要對kobject捏的樹型進行添加和刪除,希望在kobject的sysfs目錄中添加新的屬性,只需要填寫一個個attribute結構,并調用下面的函數:
int sysfs_create_file(struct kobject *kobj, struct attribute*attr);
將用attribute中的名字創建文件,并返回0,否則返回一個錯誤編碼。而下面的函數則是刪除屬性:
int sysfs_remove_file(struct kobject *kobj, struct attribute*attr);
符號鏈接:
sysfs 文件系統有通常的樹結構, 反映它代表的 kobjects 的層次組織.sysfs 子樹 (/sys/devices)代表所有的系統已知的設備, 而其他的子樹( 在 /sys/bus 之下)表示設備驅動.這些樹,不代表驅動和它們所管理的設備間的關系.展示這些附加關系需要額外的指針,指針在sysfs中通過符號連接實現.
創建符號連接:
int sysfs_create_link(struct kobject *kobj, struct kobject *target,char *name);
這個函數創建一個連接(稱為name)指向目標的sysfs入口作為一個kobj的屬性.它是一個相對連接,因此它不管sysfs在任何特殊的系統中安裝在哪里都可用.
去除符號連接可使用:
void sysfs_remove_link(struct kobject *kobj, char *name);
?
二進制屬性:
struct bin_attribute {
?
?
?
?
?
?
?
?
?
};
?
int sysfs_create_bin_file(struct kobject *kobj,struct bin_attribute *attr);
我們可以顯示的定義一個KOBJ的二進制屬性,這三個函數指針對應月SYS_***,記住這點就夠了。用的地方不是特別多,在我們PROBE一個設備的時候,可以針對該驅動的KOBJ設置二進制屬性,然后就可以在用戶空間通過VFS來訪問這個設備了。以后降到具體的驅動的例子再詳細探討。