1. Uevent的功能
Uevent是Kobject的一部分,用于在Kobject狀態(tài)發(fā)生改變時,例如增加、移除等,通知用戶空間程序。用戶空間程序收到這樣的事件后,會做相應的處理。
該機制通常是用來支持熱拔插設備的,例如U盤插入后,USB相關的驅(qū)動軟件會動態(tài)創(chuàng)建用于表示該U盤的device結(jié)構(gòu)(相應的也包括其中的kobject),并告知用戶空間程序,為該U盤動態(tài)的創(chuàng)建/dev/目錄下的設備節(jié)點,更進一步,可以通知其它的應用程序,將該U盤設備mount到系統(tǒng)中,從而動態(tài)的支持該設備。
2. Uevent在kernel中的位置
下面圖片描述了Uevent模塊在內(nèi)核中的位置:
由此可知,Uevent的機制是比較簡單的,設備模型中任何設備有事件需要上報時,會觸發(fā)Uevent提供的接口。Uevent模塊準備好上報事件的格式后,可以通過兩個途徑把事件上報到用戶空間:一種是通過kmod模塊,直接調(diào)用用戶空間的可執(zhí)行文件;另一種是通過netlink通信機制,將事件從內(nèi)核空間傳遞給用戶空間。
注1:有關kmod和netlink,會在其它文章中描述,因此本文就不再詳細說明了。
3. Uevent的內(nèi)部邏輯解析
3.1 Source Code位置
Uevent的代碼比較簡單,主要涉及kobject.h和kobject_uevent.c兩個文件,如下:
include/linux/kobject.h
lib/kobject_uevent.c
3.2 數(shù)據(jù)結(jié)構(gòu)描述
kobject.h定義了uevent相關的常量和數(shù)據(jù)結(jié)構(gòu),如下:
1: /* include/linux/kobject.h, line 50 */
2: enum kobject_action {
3: ????KOBJ_ADD,
4: ????KOBJ_REMOVE,
5: ????KOBJ_CHANGE,
6: ????KOBJ_MOVE,
7: ????KOBJ_ONLINE,
8: ????KOBJ_OFFLINE,
9: ????KOBJ_MAX
10: };
kobject_action定義了event的類型,包括:
ADD/REMOVE,Kobject(或上層數(shù)據(jù)結(jié)構(gòu))的添加/移除事件。
ONLINE/OFFLINE,Kobject(或上層數(shù)據(jù)結(jié)構(gòu))的上線/下線事件,其實是是否使能。
CHANGE,Kobject(或上層數(shù)據(jù)結(jié)構(gòu))的狀態(tài)或者內(nèi)容發(fā)生改變。
MOVE,Kobject(或上層數(shù)據(jù)結(jié)構(gòu))更改名稱或者更改Parent(意味著在sysfs中更改了目錄結(jié)構(gòu))。
CHANGE,如果設備驅(qū)動需要上報的事件不再上面事件的范圍內(nèi),或者是自定義的事件,可以使用該event,并攜帶相應的參數(shù)。
kobj_uevent_env
1: /* include/linux/kobject.h, line 31 */
2: #define UEVENT_NUM_ENVP 32 /* number of env pointers */
3: #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */
4:
5: /* include/linux/kobject.h, line 116 */
6: struct kobj_uevent_env {
7: ????char *envp[UEVENT_NUM_ENVP];
8: ????int envp_idx;
9: ????char buf[UEVENT_BUFFER_SIZE];
10:????int buflen;
11: };
前面有提到過,在利用Kmod向用戶空間上報event事件時,會直接執(zhí)行用戶空間的可執(zhí)行文件。而在Linux系統(tǒng),可執(zhí)行文件的執(zhí)行,依賴于環(huán)境變量,因此kobj_uevent_env用于組織此次事件上報時的環(huán)境變量。
envp,指針數(shù)組,用于保存每個環(huán)境變量的地址,最多可支持的環(huán)境變量數(shù)量為UEVENT_NUM_ENVP。
envp_idx,用于訪問環(huán)境變量指針數(shù)組的index。
buf,保存環(huán)境變量的buffer,最大為UEVENT_BUFFER_SIZE。
buflen,訪問buf的變量。
kset_uevent_ops
1: /* include/linux/kobject.h, line 123 */
2: struct kset_uevent_ops {
3: ????int (* const filter)(struct kset *kset, struct kobject *kobj);
4: ????const char *(* const name)(struct kset *kset, struct kobject *kobj);
5: ????int (* const uevent)(struct kset *kset, struct kobject *kobj,
6: ????????????????????????struct kobj_uevent_env *env);
7: };
kset_uevent_ops是為kset量身訂做的一個數(shù)據(jù)結(jié)構(gòu),里面包含filter和uevent兩個回調(diào)函數(shù),用處如下:
filter,當任何Kobject需要上報uevent時,它所屬的kset可以通過該接口過濾,阻止不希望上報的event,從而達到從整體上管理的目的。
name,該接口可以返回kset的名稱。如果一個kset沒有合法的名稱,則其下的所有Kobject將不允許上報uvent
uevent,當任何Kobject需要上報uevent時,它所屬的kset可以通過該接口統(tǒng)一為這些event添加環(huán)境變量。因為很多時候上報uevent時的環(huán)境變量都是相同的,因此可以由kset統(tǒng)一處理,就不需要讓每個Kobject獨自添加了。
3.3 內(nèi)部動作
通過kobject.h,uevent模塊提供了如下的API(這些API的實現(xiàn)是在"lib/kobject_uevent.c”文件中):
1: /* include/linux/kobject.h, line 206 */
2: int kobject_uevent(struct kobject *kobj, enum kobject_action action);
3: int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
4: ????????????????????????char *envp[]);
5:
6: __printf(2, 3)
7: int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...);
8:
9: int kobject_action_type(const char *buf, size_t count,
10: ????????????????????????enum kobject_action *type);
kobject_uevent_env,以envp為環(huán)境變量,上報一個指定action的uevent。環(huán)境變量的作用是為執(zhí)行用戶空間程序指定運行環(huán)境。具體動作如下:
查找kobj本身或者其parent是否從屬于某個kset,如果不是,則報錯返回(注2:由此可以說明,如果一個kobject沒有加入kset,是不允許上報uevent的)
查看kobj->uevent_suppress是否設置,如果設置,則忽略所有的uevent上報并返回(注3:由此可知,可以通過Kobject的uevent_suppress標志,管控Kobject的uevent的上報)
如果所屬的kset有uevent_ops->filter函數(shù),則調(diào)用該函數(shù),過濾此次上報(注4:這佐證了3.2小節(jié)有關filter接口的說明,kset可以通過filter接口過濾不希望上報的event,從而達到整體的管理效果)
判斷所屬的kset是否有合法的名稱(稱作subsystem,和前期的內(nèi)核版本有區(qū)別),否則不允許上報uevent
分配一個用于此次上報的、存儲環(huán)境變量的buffer(結(jié)果保存在env指針中),并獲得該Kobject在sysfs中路徑信息(用戶空間軟件需要依據(jù)該路徑信息在sysfs中訪問它)
調(diào)用add_uevent_var接口(下面會介紹),將Action、路徑信息、subsystem等信息,添加到env指針中
如果傳入的envp不空,則解析傳入的環(huán)境變量中,同樣調(diào)用add_uevent_var接口,添加到env指針中
如果所屬的kset存在uevent_ops->uevent接口,調(diào)用該接口,添加kset統(tǒng)一的環(huán)境變量到env指針
根據(jù)ACTION的類型,設置kobj->state_add_uevent_sent和kobj->state_remove_uevent_sent變量,以記錄正確的狀態(tài)
調(diào)用add_uevent_var接口,添加格式為"SEQNUM=%llu”的序列號
如果定義了"CONFIG_NET”,則使用netlink發(fā)送該uevent
以uevent_helper、subsystem以及添加了標準環(huán)境變量(HOME=/,PATH=/sbin:/bin:/usr/sbin:/usr/bin)的env指針為參數(shù),調(diào)用kmod模塊提供的call_usermodehelper函數(shù),上報uevent。?
其中uevent_helper的內(nèi)容是由內(nèi)核配置項CONFIG_UEVENT_HELPER_PATH(位于./drivers/base/Kconfig)決定的(可參考lib/kobject_uevent.c, line 32),該配置項指定了一個用戶空間程序(或者腳本),用于解析上報的uevent,例如"/sbin/hotplug”。?
call_usermodehelper的作用,就是fork一個進程,以uevent為參數(shù),執(zhí)行uevent_helper。
kobject_uevent,和kobject_uevent_env功能一樣,只是沒有指定任何的環(huán)境變量。
add_uevent_var,以格式化字符的形式(類似printf、printk等),將環(huán)境變量copy到env指針中。
kobject_action_type,將enum kobject_action類型的Action,轉(zhuǎn)換為字符串。
說明:怎么指定處理uevent的用戶空間程序(簡稱uevent helper)??
上面介紹kobject_uevent_env的內(nèi)部動作時,有提到,Uevent模塊通過Kmod上報Uevent時,會通過call_usermodehelper函數(shù),調(diào)用用戶空間的可執(zhí)行文件(或者腳本,簡稱uevent helper?)處理該event。而該uevent helper的路徑保存在uevent_helper數(shù)組中。?
可以在編譯內(nèi)核時,通過CONFIG_UEVENT_HELPER_PATH配置項,靜態(tài)指定uevent helper。但這種方式會為每個event fork一個進程,隨著內(nèi)核支持的設備數(shù)量的增多,這種方式在系統(tǒng)啟動時將會是致命的(可以導致內(nèi)存溢出等)。因此只有在早期的內(nèi)核版本中會使用這種方式,現(xiàn)在內(nèi)核不再推薦使用該方式。因此內(nèi)核編譯時,需要把該配置項留空。?
在系統(tǒng)啟動后,大部分的設備已經(jīng)ready,可以根據(jù)需要,重新指定一個uevent helper,以便檢測系統(tǒng)運行過程中的熱拔插事件。這可以通過把helper的路徑寫入到"/sys/kernel/uevent_helper”文件中實現(xiàn)。實際上,內(nèi)核通過sysfs文件系統(tǒng)的形式,將uevent_helper數(shù)組開放到用戶空間,供用戶空間程序修改訪問,具體可參考"./kernel/ksysfs.c”中相應的代碼,這里不再詳細描述。
?
評論
查看更多