设备模型2
前言
在上一篇中,我們大致描述了LINUX設(shè)備模型,我們先來總結(jié)一下三要素的關(guān)系。
?
?
從圖中可以看出,Linux設(shè)備模型就是"總線、設(shè)備、驅(qū)動、類"這四個概念之前的相互關(guān)系;這也是Linux2.6內(nèi)核抽象出來的用于管理系統(tǒng)中所有設(shè)備的模型圖;
簡單地描述設(shè)備模型的層次關(guān)系如下:
1、驅(qū)動核心中可以注冊多種類型的總線(bus_type);
2、每一種類型的總線下面可以掛載許多設(shè)備(device);
3、每一種類型的總線可以使用很多設(shè)備驅(qū)動(device_driver);
4、每一個驅(qū)動程序可以管理一組設(shè)備;
這種基本關(guān)系的建立源于實際系統(tǒng)中各種總線、設(shè)備、驅(qū)動、類結(jié)構(gòu)的抽象;
?
設(shè)備模型之kobject、kset
?
kobject是設(shè)備模型中一個很基本的概念,最初支持為了支持引用計數(shù),但是隨著時間的轉(zhuǎn)移,它承擔(dān)了越來越多的任務(wù):
1)對象的引用計數(shù)
2)SYS表述
3)數(shù)據(jù)結(jié)構(gòu)關(guān)聯(lián)
4)熱插拔處理
?
?
它的定義如下:
struct kobject {
//名稱
?constchar??*name;
?struct list_head?entry;
//指向kobject的父對象,以此來在內(nèi)核中構(gòu)造一個對象層次結(jié)構(gòu),并且可以將多個對象之間的關(guān)系表現(xiàn)初來,這就是sysfs的真相:一個用戶空間的文件系統(tǒng),用來表示內(nèi)核中kobject對象的層次結(jié)構(gòu)。
?structkobject??*parent;
?structkset??*kset;
?struct kobj_type?*ktype;
?struct sysfs_dirent?*sd;
?structkref??kref;
?unsigned int state_initialized:1;
?unsigned int state_in_sysfs:1;
?unsigned int state_add_uevent_sent:1;
?unsigned int state_remove_uevent_sent:1;
?unsigned int uevent_suppress:1;
};
一個kobject存在的意義在于把高級對象連接到設(shè)備模型上。kobject正如最頂層的基類,而其他類則是派生物,它實現(xiàn)了一系列方法,對自身并沒有額數(shù)的作用,但是對其他對象卻非常有效。
這句話可以這么理解,在LINUX中,是用C語言實現(xiàn)的,沒有對象的概念,但是kobject被包含在各個結(jié)構(gòu)中,就如同面向?qū)ο蟮牡幕?#xff0c;根據(jù)它描述的父子兄弟關(guān)系,把各個結(jié)構(gòu)實例聯(lián)系起來,想成目錄結(jié)構(gòu)。現(xiàn)在回過頭去看看總線,設(shè)備,驅(qū)動的結(jié)構(gòu)定義中其實都有這個KOBJECT的影子,只是在前面我們分析的時候沒有把列出來。
kobject的初始化:
1首先使用memest函數(shù)將整個kobject清零。
2調(diào)用kobject_init()函數(shù)。設(shè)置結(jié)構(gòu)內(nèi)部的一些成員。void kobject_init(struct kobject*kobj);kobject_init設(shè)置kobject的引用計數(shù)為 1。
3設(shè)置kobject的名字,這是sysfs入口中使用的名字。int kobject_set_name(struct kobject*kobj, const char *format, ...)
kobject的引用計數(shù):
kobject 的其中一個關(guān)鍵函數(shù)是作為一個引用計數(shù)器, 給一個它被嵌入的對象. 只要對這個對象的引用存在, 這個對象(和支持它的代碼)必須繼續(xù)存在.
struct kobject *kobject_get(struct kobject *kobj);
void kobject_put(struct kobject *kobj);
釋放函數(shù)和 kobject 類型:
通知由 kobject 的一個釋放函數(shù)來完成. 常常地, 這個方法有一個形式如下:
void my_object_release(struct kobject *kobj)
{
?? ?structmy_object *mine = container_of(kobj, struct my_object, kobj);
???kfree(mine);
}
有一點需要注意:每一個kobject都必須有一個release方法,并且kobject在該方法被調(diào)用前必須保持不變。
而需要在意一點,release函數(shù)并沒有包含在kobject自身內(nèi),而是與包含kobject的結(jié)構(gòu)類型相關(guān)聯(lián)的kobj_type數(shù)據(jù)結(jié)構(gòu)負(fù)責(zé)對該類型進(jìn)行跟蹤。
注意結(jié)構(gòu)中的ktype,以及SYSFS_OPS,見下邊SYS相關(guān)的分析。
struct kobj_type
{
?? ?void(*release)(struct kobject *);//保存kobject類型的release函數(shù)
?? ?structsysfs_ops *sysfs_ops;
?? ?structattribute ** default_attrs;
}
kobject層次結(jié)構(gòu)、kset
內(nèi)核用kobject結(jié)構(gòu)將各個對象連接起來組成一個分層的結(jié)構(gòu)體系,從而與模型化的子系統(tǒng)相匹配。有兩種獨(dú)立的機(jī)制用于連接:parent指針和kset
在kobject結(jié)構(gòu)的parent成員中,保存了另外一個kobject結(jié)構(gòu)的指針,這個結(jié)構(gòu)表示了分層結(jié)構(gòu)中上一層的節(jié)點,而parent最重要的用途是在sysfs分層結(jié)構(gòu)中定位對象。
kset是嵌入相同類型結(jié)構(gòu)的kobject集合,但是不同之處在于,kobject在乎的是對象的類型,而kset關(guān)心的是對象的集合與聚合。需要注意的是,kset總是在sysfs中出現(xiàn),一旦設(shè)置了kset并把它添加到系統(tǒng)中,將在sysfs中創(chuàng)建一個目錄。kobject不必在sysfs中表示,但是kset中的每一個kobject成員都將在sysfs中得到表述。
創(chuàng)建一個對象時,要把一個kobject添加到kset中,要先將kobject的kset成員指向目的的kset,調(diào)用extern intkobject_register(struct kobject *kobj);
這個函數(shù)僅僅是一個 kobject_init 和 kobject_add 的結(jié)合.
struct kset {
?struct list_head list;
?spinlock_t list_lock;
?struct kobject kobj;
?const struct kset_uevent_ops *uevent_ops;
};
對于初始化和設(shè)置, 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 的引用計數(shù), 情況大概相同:
struct kset *kset_get(struct kset *kset);
void kset_put(struct kset *kset);
一個 kset 還有一個名子, 存儲于嵌入的 kobject. 因此, 如果你有一個 kset 稱為 my_set,你將設(shè)置它的名子用:
kobject_set_name(&my_set->kobj, "Thename");
ksets 還有一個指針( 在 ktye 成員 )指向 kobject_type 結(jié)構(gòu)來描述它包含的 kobject.這個類型優(yōu)先于在 kobject 自身中的 ktype 成員. 結(jié)果, 在典型的應(yīng)用中, 在 struct kobject 中的ktype 成員被留為 NULL, 因為 kset 中的相同成員是實際使用的那個.
低層sysfs操作
(什么是SYSFS?? 我們就把它先看做設(shè)備文件系統(tǒng),用來管理系統(tǒng)的各種設(shè)備,驅(qū)動,總線,類的文件系統(tǒng))。
kobject隱藏在sysfs文件系統(tǒng)之后的機(jī)制,對于sysfs每個目錄,內(nèi)核中都會存在一個對應(yīng)的kobject。每一個kobject都輸出一個或者多個屬性。在sysfs目錄中表現(xiàn)為文件,其中的內(nèi)容由內(nèi)核生成。在<linux/sysfs.h>中包含了sysfs工作代碼。
使kobject在sysfs出現(xiàn)僅僅是調(diào)用kobject_add的事情.kobjects的sysfs入口一直為目錄,因此一個對kobject_add的調(diào)用導(dǎo)致在sysfs中創(chuàng)建一個目錄.常常地, 這個目錄包含一個或多個屬性;
分配給 kobject 的名子( 用 kobject_set_name ) 是給 sysfs 目錄使用的名子. 因此, 出現(xiàn)在sysfs 層次的相同部分的 kobjects 必須有獨(dú)特的名子. 分配給 kobjects 的名子也應(yīng)當(dāng)是合理的文件名子:它們不能包含斜線字符, 并且空白的使用強(qiáng)烈不推薦.
sysfs 入口位于對應(yīng) kobject 的 parent 指針的目錄中. 如果 parent 是 NULL 當(dāng)kobject_add 被調(diào)用時, 它被設(shè)置為嵌在新 kobject 的 kset 中的 kobject;
當(dāng)被創(chuàng)建時, 每個kobject被給定一套缺省屬性. 這些屬性通過kobj_type結(jié)構(gòu)來指定.
default_attr 成員列舉了對每個這樣類型的 kobject 被創(chuàng)建的屬性, 并且 sysfs_ops提供方法來實現(xiàn)這些屬性.
struct attribute {
?char *name;
?struct module *owner;
?mode_t mode;
};
在這個結(jié)構(gòu)中, name 是屬性的名子( 如同它出現(xiàn)在 kobject 的 sysfs 目錄中), owner是一個指向模塊的指針(如果有一個), 模塊負(fù)責(zé)這個屬性的實現(xiàn), 并且 mode 是應(yīng)用到這個屬性的保護(hù)位. mode 常常是S_IRUGO 對于只讀屬性; 如果這個屬性是可寫的, 你可以扔出 S_IWUSR 來只給 root 寫權(quán)限( modes 的宏定義在<linux/stat.h> 中). default_attrs列表中的最后一個入口必須用 0 填充.
實現(xiàn)這些屬性則需要kobj_type->sysfs_ops成員, 它指向一個結(jié)構(gòu), 定義為:
struct sysfs_ops {
?ssize_t (*show)(struct kobject *kobj, structattribute *attr, char *buffer);
?ssize_t (*store)(struct kobject *kobj, structattribute *attr, const char *buffer, size_t size);
};
無論何時一個屬性從用戶空間讀取, show 方法被用一個指向 kobject 的指針和適當(dāng)?shù)膶傩越Y(jié)構(gòu)來調(diào)用.這個方法應(yīng)當(dāng)將給定屬性值編碼進(jìn)緩沖, 要確定沒有覆蓋它( 它是 PAGE_SIZE 字節(jié)), 并且返回實際的被返回數(shù)據(jù)的長度.sysfs 的慣例表明每個屬性應(yīng)當(dāng)包含一個單個的, 人可讀的值; 如果你有許多消息返回, 你可要考慮將它分為多個屬性.
同樣的 show 方法用在所有的和給定 kobject 關(guān)聯(lián)的屬性. 傳遞到函數(shù)的 attr 指針可用來決定需要哪個屬性. 一些show 方法包含對屬性名子的一系列測試. 其他的實現(xiàn)將屬性結(jié)構(gòu)嵌入另一個結(jié)構(gòu), 來包含需要返回屬性值的信息; 在這種情況下,container_of 可能用在 show 方法中來獲得一個指向嵌入結(jié)構(gòu)的指針.
store 方法類似; 它應(yīng)當(dāng)將存在緩沖的數(shù)據(jù)編碼( size 包含數(shù)據(jù)的長度, 這不能超過 PAGE_SIZE ),存儲和以任何有意義的的方式響應(yīng)新數(shù)據(jù), 并且返回實際編碼的字節(jié)數(shù). store 方法只在屬性的許可允許寫才被調(diào)用. 當(dāng)編寫一個store 方法時, 不要忘記你在接收來自用戶空間的任意信息; 你應(yīng)當(dāng)在采取對應(yīng)動作之前非常小心地驗證它. 如果到數(shù)據(jù)不匹配期望,返回一個負(fù)的錯誤值, 而不是可能地做一些不想要的和無法恢復(fù)的事情. 如果你的設(shè)備輸出一個自銷毀的屬性,你應(yīng)當(dāng)要求一個特定的字符串寫到那里來引發(fā)這個功能; 一個偶然的, 隨機(jī)寫應(yīng)當(dāng)只產(chǎn)生一個錯誤.
非默認(rèn)屬性:
多數(shù)情況下,kobject類型的default_attrs成員描述了kobject擁有的所有屬性。但是我們還可以根據(jù)需要對kobject捏的樹型進(jìn)行添加和刪除,希望在kobject的sysfs目錄中添加新的屬性,只需要填寫一個個attribute結(jié)構(gòu),并調(diào)用下面的函數(shù):
int sysfs_create_file(struct kobject *kobj, struct attribute*attr);
將用attribute中的名字創(chuàng)建文件,并返回0,否則返回一個錯誤編碼。而下面的函數(shù)則是刪除屬性:
int sysfs_remove_file(struct kobject *kobj, struct attribute*attr);
符號鏈接:
sysfs 文件系統(tǒng)有通常的樹結(jié)構(gòu), 反映它代表的 kobjects 的層次組織.sysfs 子樹 (/sys/devices)代表所有的系統(tǒng)已知的設(shè)備, 而其他的子樹( 在 /sys/bus 之下)表示設(shè)備驅(qū)動.這些樹,不代表驅(qū)動和它們所管理的設(shè)備間的關(guān)系.展示這些附加關(guān)系需要額外的指針,指針在sysfs中通過符號連接實現(xiàn).
創(chuàng)建符號連接:
int sysfs_create_link(struct kobject *kobj, struct kobject *target,char *name);
這個函數(shù)創(chuàng)建一個連接(稱為name)指向目標(biāo)的sysfs入口作為一個kobj的屬性.它是一個相對連接,因此它不管sysfs在任何特殊的系統(tǒng)中安裝在哪里都可用.
去除符號連接可使用:
void sysfs_remove_link(struct kobject *kobj, char *name);
?
二進(jìn)制屬性:
struct bin_attribute {
?struct attribute?attr;
?size_t???size;
?void???*private;
?ssize_t (*read)(struct file *, struct kobject *,struct bin_attribute *,
???char *,loff_t, size_t);
?ssize_t (*write)(struct file *,struct kobject *,struct bin_attribute *,
??? char *,loff_t, size_t);
?int (*mmap)(struct file *, struct kobject *,struct bin_attribute *attr,
?????struct vm_area_struct *vma);
};
?
int sysfs_create_bin_file(struct kobject *kobj,struct bin_attribute *attr); 我們可以顯示的定義一個KOBJ的二進(jìn)制屬性,這三個函數(shù)指針對應(yīng)月SYS_***,記住這點就夠了。用的地方不是特別多,在我們PROBE一個設(shè)備的時候,可以針對該驅(qū)動的KOBJ設(shè)置二進(jìn)制屬性,然后就可以在用戶空間通過VFS來訪問這個設(shè)備了。以后降到具體的驅(qū)動的例子再詳細(xì)探討。創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎
總結(jié)
- 上一篇: 数学家刘徽李善兰陈景润华罗庚
- 下一篇: 酷派D530刷机指引之民间ROM