W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
Kobject 是基礎(chǔ)的結(jié)構(gòu), 它保持設(shè)備模型在一起. 初始地它被作為一個(gè)簡(jiǎn)單的引用計(jì)數(shù), 但是它的責(zé)任已隨時(shí)間增長, 并且因此有了它自己的戰(zhàn)場(chǎng). struct kobject 所處理的任務(wù)和它的支持代碼現(xiàn)在包括:
對(duì)象的引用計(jì)數(shù)
常常, 當(dāng)一個(gè)內(nèi)核對(duì)象被創(chuàng)建, 沒有方法知道它會(huì)存在多長時(shí)間. 一種跟蹤這種對(duì)象生命周期的方法是通過引用計(jì)數(shù). 當(dāng)沒有內(nèi)核代碼持有對(duì)給定對(duì)象的引用, 那個(gè)對(duì)象已經(jīng)完成了它的有用壽命并且可以被刪除.
sysfs 表示
在 sysfs 中出現(xiàn)的每個(gè)對(duì)象在它的下面都有一個(gè) kobject, 它和內(nèi)核交互來創(chuàng)建它的可見表示.
數(shù)據(jù)結(jié)構(gòu)粘和
設(shè)備模型是, 整體來看, 一個(gè)極端復(fù)雜的由多級(jí)組成的數(shù)據(jù)結(jié)構(gòu), 各級(jí)之間有許多連接. kobject 實(shí)現(xiàn)這個(gè)結(jié)構(gòu)并且保持它在一起.
熱插拔事件處理
kobject 子系統(tǒng)處理事件的產(chǎn)生, 事件通知用戶空間關(guān)于系統(tǒng)中硬件的來去.
你可能從前面的列表總結(jié)出 kobject 是一個(gè)復(fù)雜的結(jié)構(gòu). 這可能是對(duì)的. 通過一次看一部分, 但是, 是有可能理解這個(gè)結(jié)構(gòu)和它如何工作的.
一個(gè) kobject 有類型 struct kobject; 它在 <linux/kobject.h> 中定義. 這個(gè)文件還包含許多其他和 kobject 相關(guān)的結(jié)構(gòu)的聲明, 一個(gè)操作它們的函數(shù)的長列表.
在我們進(jìn)入細(xì)節(jié)前, 值得花些時(shí)間理解如何使用 kobjects. 如果你回看被 kobjects 處理的函數(shù)列表, 你會(huì)看到它們都是代表其他對(duì)象進(jìn)行的服務(wù). 一個(gè) kobject, 換句話說, 對(duì)其自己很少感興趣; 它存在僅僅為了結(jié)合一個(gè)高級(jí)對(duì)象到設(shè)備模型.
因此, 對(duì)于內(nèi)核代碼它很少(甚至不知道)創(chuàng)建一個(gè)孤立的 kobject; 相反, kobject 被用來控制存取更大的, 特定域的對(duì)象. 為此, kobject 被嵌入到其他結(jié)構(gòu)中. 如果你習(xí)慣以面向?qū)ο蟮男g(shù)語考慮事情, kobject 可被看作一個(gè)頂級(jí)的, 抽象類, 其他的類自它而來. 一個(gè) kobject 實(shí)現(xiàn)一系列功能, 這些功能對(duì)自己不是特別有用而對(duì)其他對(duì)象是好的. C 語言不允許直接表達(dá)繼承, 因此其他的技術(shù) -- 例如將一個(gè)結(jié)構(gòu)嵌入另一個(gè) -- 必須使用.
作為一個(gè)例子, 讓我們回看 struct cdev, 我們?cè)诘?3 章遇到過它. 那個(gè)結(jié)構(gòu), 如同在 2.6.10 內(nèi)核中發(fā)現(xiàn)的, 看來如此:
struct cdev {
struct kobject kobj;
struct module *owner;
struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
我們可以看出, cdev 結(jié)構(gòu)有一個(gè) kobject 嵌在里面. 如果你有一個(gè)這樣的結(jié)構(gòu), 會(huì)發(fā)現(xiàn)它的嵌入的 kobject 只是使用 kobj 成員. 使用 kobjects 的代碼有相反的問題, 但是: 如果一個(gè) struct kobject 指針, 什么是指向包含結(jié)構(gòu)的指針? 你應(yīng)當(dāng)避免竅門(例如假定 kobject 是在結(jié)構(gòu)的開始), 并且, 相反, 使用 container_of 宏 (在第 3 章的"open 方法"一節(jié)中介紹的). 因此轉(zhuǎn)換一個(gè)指向嵌在一個(gè)結(jié)構(gòu) cdev 中的一個(gè) struct kobject 的指針 kp 的方法是:
struct cdev *device = container_of(kp, struct cdev, kobj);
程序員常常定義一個(gè)簡(jiǎn)單的宏來"后向轉(zhuǎn)換" kobject 指針到包含類型.
本書已經(jīng)展示了許多數(shù)據(jù)類型, 帶有簡(jiǎn)單的在編譯或者運(yùn)行時(shí)初始化機(jī)制. 一個(gè) kobject 的初始化有些復(fù)雜, 特別當(dāng)使用它的所有函數(shù)時(shí). 不管一個(gè) kobject 如何使用, 但是, 必須進(jìn)行幾個(gè)步驟.
這些步驟的第一個(gè)是僅僅設(shè)置整個(gè) kobject 為 0, 常常使用一個(gè)對(duì) memset 的調(diào)用. 常常這個(gè)初始化作為清零這個(gè) kobjiect 嵌入的結(jié)構(gòu)的一部分. 清零一個(gè) kobject 失敗導(dǎo)致非常奇怪的崩潰, 進(jìn)一步會(huì)掉線; 這不是你想跳過的一步.
下一步是設(shè)立一些內(nèi)部成員, 使用對(duì) kobject_init() 的調(diào)用:
void kobject_init(struct kobject *kobj);
在其他事情中, kobject_init 設(shè)置 kobject 的引用計(jì)數(shù)為 1. 調(diào)用 kobject_init 不夠, 但是. kobject 用戶必須, 至少, 設(shè)置 kobject 的名子. 這是用在 sysfs 入口的名子. 如果你深入內(nèi)核代碼, 你可以發(fā)現(xiàn)直接拷貝一個(gè)字符串到 kobject 的名子成員的代碼, 但是應(yīng)當(dāng)避免這個(gè)方法. 相反, 使用:
int kobject_set_name(struct kobject *kobj, const char *format, ...);
這個(gè)函數(shù)采用一個(gè) printk 風(fēng)格的變量參數(shù)列表. 不管你信或不信, 對(duì)這種操作實(shí)際上可能失敗( 他可能試圖分配內(nèi)存 ); 負(fù)責(zé)任的代碼應(yīng)當(dāng)檢查返回值并且有針對(duì)性的相應(yīng).
其他的由創(chuàng)建者應(yīng)當(dāng)設(shè)置的 kobject 成員, 直接或間接, 是 ktype, kset, 和 parent. 我們?cè)诒菊律院蟮竭@些.
一個(gè) kobject 的其中一個(gè)關(guān)鍵函數(shù)是作為一個(gè)引用計(jì)數(shù)器, 給一個(gè)它被嵌入的對(duì)象. 只要對(duì)這個(gè)對(duì)象的引用存在, 這個(gè)對(duì)象( 和支持它的代碼) 必須繼續(xù)存在. 來操作一個(gè) kobject 的引用計(jì)數(shù)的低級(jí)函數(shù)是:
struct kobject *kobject_get(struct kobject *kobj);
void kobject_put(struct kobject *kobj);
一個(gè)對(duì) kobject_get 的成功調(diào)用遞增 kobject 的 引用計(jì)數(shù)并且返回一個(gè)指向 kobject 的指針. 如果, 但是, 這個(gè) kobject 已經(jīng)在被銷毀的過程中, 這個(gè)操作失敗, 并且 kobject_get 返回 NULL. 這個(gè)返回值必須總是被測(cè)試, 否則可能導(dǎo)致無法結(jié)束的令人不愉快的競(jìng)爭(zhēng)情況.
當(dāng)一個(gè)引用被釋放, 對(duì) kobject_put 的調(diào)用遞減引用計(jì)數(shù), 并且可能地, 釋放這個(gè)對(duì)象. 記住 kobject _init 設(shè)置這個(gè)引用計(jì)數(shù)為 1; 因此當(dāng)你創(chuàng)建一個(gè) kobject, 你應(yīng)當(dāng)確保對(duì)應(yīng)地采取 kobject_put 調(diào)用, 當(dāng)這個(gè)初始化引用不再需要.
注意, 在許多情況下, 在 kobject 自身中的引用計(jì)數(shù)可能不足以阻止競(jìng)爭(zhēng)情況. 一個(gè) kobject 的存在( 以及它的包含結(jié)構(gòu) ) 可能非常, 例如, 需要?jiǎng)?chuàng)建這個(gè) kobject 的模塊的繼續(xù)存在. 在這個(gè) kobject 仍然在被傳送時(shí)不能卸載那個(gè)模塊. 這是為什么我們上面看到的 cdev 結(jié)構(gòu)包含一個(gè) struct module 指針. struct cdev 的引用計(jì)數(shù)實(shí)現(xiàn)如下:
struct kobject *cdev_get(struct cdev *p)
{
struct module *owner = p->owner;
struct kobject *kobj;
if (owner && !try_module_get(owner))
return NULL;
kobj = kobject_get(&p->kobj);
if (!kobj)
module_put(owner);
return kobj;
}
創(chuàng)建一個(gè)對(duì) cdev 結(jié)構(gòu)的引用還需要?jiǎng)?chuàng)建一個(gè)對(duì)擁有它的模塊的引用. 因此, cdev_get 使用 try_module_get 來試圖遞增這個(gè)模塊的使用計(jì)數(shù). 如果這個(gè)操作成功, kobject_get 被同樣用來遞增 kobject 的引用計(jì)數(shù). 那個(gè)操作可能失敗, 當(dāng)然, 因此這個(gè)代碼檢查自 kobject_get 的返回值并且釋放它的對(duì)模塊的引用如果事情沒有解決.
討論中仍然缺失的一個(gè)重要事情是當(dāng)一個(gè) kobject 的引用計(jì)數(shù)到 0 時(shí)會(huì)發(fā)生什么. 創(chuàng)建 kobject 的代碼通常不知道什么時(shí)候要發(fā)生這個(gè)情況; 如果它知道, 在第一位使用一個(gè)引用計(jì)數(shù)就沒有意義了. 即便當(dāng)引入 sysfs 時(shí)可預(yù)測(cè)的對(duì)象生命周期變得更加復(fù)雜; 用戶空間程序可保持一個(gè)對(duì) kobject 的引用( 通過保持一個(gè)它的關(guān)聯(lián)的 sysfs 文件打開 )一段任意的時(shí)間.
最后的結(jié)果是一個(gè)被 kobject 保護(hù)的結(jié)構(gòu)無法在任何一個(gè)單個(gè)的, 可預(yù)測(cè)的驅(qū)動(dòng)生命周期中的點(diǎn)被釋放, 但是可以在必須準(zhǔn)備在 kobject 的引用計(jì)數(shù)到 0 的任何時(shí)刻運(yùn)行的代碼中. 引用計(jì)數(shù)不在創(chuàng)建 kobject 的代碼的直接控制之下. 因此這個(gè)代碼必須被異步通知, 無論何時(shí)對(duì)它的 kobject 的最后引用消失.
這個(gè)通知由 kobject 的一個(gè)釋放函數(shù)來完成. 常常地, 這個(gè)方法有一個(gè)形式如下:
void my_object_release(struct kobject *kobj)
{
struct my_object *mine = container_of(kobj, struct my_object, kobj);
/* Perform any additional cleanup on this object, then... */
kfree(mine);
}
要強(qiáng)調(diào)的重要一點(diǎn)是: 每個(gè) kobject 必須有一個(gè)釋放函數(shù), 并且這個(gè) kobject 必須持續(xù)( 以一致的狀態(tài) ) 直到這個(gè)方法被調(diào)用. 如果這些限制不滿足, 代碼就有缺陷. 當(dāng)這個(gè)對(duì)象還在使用時(shí)被釋放會(huì)有風(fēng)險(xiǎn), 或者在最后引用被返回后無法釋放對(duì)象.
有趣的是, 釋放方法沒有存儲(chǔ)在 kobject 自身里面; 相反, 它被關(guān)聯(lián)到包含 kobject 的結(jié)構(gòu)類型中. 這個(gè)類型被跟蹤, 用一個(gè) struct kobj_type 結(jié)構(gòu)類型, 常常簡(jiǎn)單地稱為一個(gè) "ktype". 這個(gè)結(jié)構(gòu)看來如下:
struct kobj_type {
void (*release)(struct kobject *);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
在 struct kobj_type 中的 release 成員是, 當(dāng)然, 一個(gè)指向這個(gè) kobject 類型的 release 方法的指針. 我們將回到其他 2 個(gè)成員( sysfs_ops 和 default_attrs )在本章后面.
每一個(gè) kobject 需要有一個(gè)關(guān)聯(lián)的 kobj_type 結(jié)構(gòu). 易混淆地, 指向這個(gè)結(jié)構(gòu)的指針能在 2 個(gè)不同的地方找到. kobject 結(jié)構(gòu)自身包含一個(gè)成員(稱為 ktype)包含這個(gè)指針. 但是, 如果這個(gè) kobject 是一個(gè) kset 的成員, kobj_type 指針由 kset 提供. ( 我們將在下一節(jié)查看 ksets. ) 其間, 這個(gè)宏定義:
struct kobj_type *get_ktype(struct kobject *kobj); finds the kobj_type pointer for a given kobject.
kobject 結(jié)構(gòu)常常用來連接對(duì)象到一個(gè)層級(jí)的結(jié)構(gòu)中, 匹配正被建模的子系統(tǒng)的結(jié)構(gòu). 有 2 個(gè)分開的機(jī)制對(duì)于這個(gè)連接: parent 指針和 ksets.
在結(jié)構(gòu) kobject 中的 parent 成員是一個(gè)指向其他對(duì)象的指針 -- 代表在層次中之上的下一級(jí). 如果, 例如, 一個(gè) kobject 表示一個(gè) USB 設(shè)備, 它的 parent 指針可能指示這個(gè)設(shè)備被插入的 hub.
parent 指針的主要用途是在 sysfs 層次中定位對(duì)象. 我們將看到這個(gè)如何工作, 在"低級(jí) sysfs 操作"一節(jié)中.
很多情況, 一個(gè) kset 看來象一個(gè) kobj_type 結(jié)構(gòu)的擴(kuò)展; 一個(gè) kset 是一個(gè)嵌入到相同類型結(jié)構(gòu)的 kobject 的集合. 但是, 雖然 struct kobj_type 關(guān)注的是一個(gè)對(duì)象的類型, struct kset 被聚合和集合所關(guān)注. 這 2 個(gè)概念已被分開以至于一致類型的對(duì)象可以出現(xiàn)在不同的集合中.
因此, 一個(gè) kset 的主要功能是容納; 它可被當(dāng)作頂層的給 kobjects 的容器類. 實(shí)際上, 每個(gè) kset 在內(nèi)部容納它自己的 kobject, 并且它可以, 在許多情況下, 如同一個(gè) kobject 相同的方式被對(duì)待. 值得注意的是 ksets 一直在 sysfs 中出現(xiàn); 一旦一個(gè) kset 已被建立并且加入到系統(tǒng), 會(huì)有一個(gè) sysfs 目錄給它. kobjects 沒有必要在 sysfs 中出現(xiàn), 但是每個(gè)是 kset 成員的 kobject 都出現(xiàn)在那里.
增加一個(gè) kobject 到一個(gè) kset 常常在一個(gè)對(duì)象創(chuàng)建時(shí)完成; 它是一個(gè) 2 步的過程. kobject 的 kset 成員必須 ???; 接著kobject 應(yīng)當(dāng)被傳遞到:
int kobject_add(struct kobject *kobj);
如常, 程序員應(yīng)當(dāng)小心這個(gè)函數(shù)可能失敗(在這個(gè)情況下它返回一個(gè)負(fù)錯(cuò)誤碼)并且相應(yīng)地反應(yīng). 有一個(gè)內(nèi)核提供的方便函數(shù):
extern int kobject_register(struct kobject *kobj);
這個(gè)函數(shù)僅僅是一個(gè) kobject_init 和 kobject_add 的結(jié)合.
當(dāng)一個(gè) kobject 被傳遞給 kobject_add, 它的引用計(jì)數(shù)被遞增. kset 中容納的, 畢竟, 是一個(gè)對(duì)這個(gè)對(duì)象的引用. 某種意義上, kobject 可能要必須從 kset 中移出來清除這個(gè)引用; 完成這個(gè)使用:
void kobject_del(struct kobject *kobj);
還有一個(gè) kobject_unregister 函數(shù), 是 kobject_del 和 kobject_put 的結(jié)合.
一個(gè) kset 保持它的子女在一個(gè)標(biāo)準(zhǔn)的內(nèi)核鏈表中. 在大部分情況下, 被包含的 kobjects 也有指向這個(gè) kset 的指針( 或者, 嚴(yán)格地, 它的嵌入 kobject)在它們的 parent 的成員. 因此, 典型地, 一個(gè) kset 和它的 kobjects 看來有些象你在圖 一個(gè)簡(jiǎn)單的 kset 層次中所見. 記住:
圖?14.2.?一個(gè)簡(jiǎn)單的 kset 層次
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: