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