免費谘詢熱線
13621929115作(zuò)者:wowo發布於:2014-3-10 20:39 分類:統一設備模型題圖轉自(zì):Linux設備模型(3)_Ueventwww.wowotech.net/devi設(shè)備模(mó)型(xíng)ce_model/uevent.html
轉載者注:本文在第一(yī)次閱讀時,建議初接觸內(nèi)核代碼的小白最好不要(yào)太死摳代(dài)碼,先明白大致的(de)功(gōng)能,頭(tóu)腦中形成一個簡單的認識,在不斷學習中逐漸深入文章內的代碼為4.14版本的內核,與原作(zuò)者使用的內核,略(luè)有差別本文(wén)主體是原作者文章,小編進(jìn)行(háng)進一步的排版、內容增添、內容注設備模型釋,後期會根據自己的感悟用更多流程圖、思維導圖之類的框圖表達自己(jǐ)的理解,敬請(qǐng)期待!!!
1. Uevent的功能Uevent是Kobject的一(yī)部分,用於在Kobject狀態發生改變時,例如增加、移(yí)除等,通知用戶空間程序用戶空間程(chéng)序收到這樣的事件後,會做(zuò)相應的處理該機(jī)製通常是用來支持熱拔插(chā)設備的,例如設(shè)備模(mó)型(xíng)U盤插入後,USB相關的驅動軟件會動態創建用於表(biǎo)示該U盤的device結構(相應的也包括其中(zhōng)的kobject),並告知用戶空間程序,為該U盤動(dòng)態的創建/dev/目錄下的設備節點,更進一步,可以通知其(qí)它的應用程序,將該U盤設備mount到係統中,從而動態(tài)的支持該設備。
2. Uevent在kernel中設備模型的位置下麵圖片描述了Uevent模塊在內核中的位置:
由此可知,Uevent的機(jī)製是比較簡(jiǎn)單的,設備模型中任何設備有事件需要(yào)上報時,會觸發Uevent提供的接口Uevent模塊準備好上報事(shì)件的格式後(hòu),可以(yǐ)通過兩個途徑把事件上(shàng)報到用戶空間:一種是通過kmod模塊,直接調用用(yòng)戶空間的可執行文件;另一種是通設備模型(xíng)過netlink通信機製,將事件從內核空(kōng)間傳遞給用戶空間。
注1:有(yǒu)關kmod和netlink,會在其(qí)它文章中(zhōng)描述,因此本文就不再詳細說明了3. Uevent的內部(bù)邏輯(jí)解析3.1 Source Code位置(zhì)Uevent的代碼比較簡單,主要涉及kobject.h和kobject_uevent.c兩個文件設備模型,如下:。
include/linux/kobject.hlib/kobject_uevent.c3.2 數據結構描述kobject.h定義了uevent相關的常量和數據結構,如下:kobject_action1:
/* include/linux/kobject.h, line 50 */2:enum設備模型(xíng)kobject_action{3:KOBJ_ADD,4:KOBJ_REMOVE,5:KOBJ_CHANGE,6:KOBJ_MOVE
,7:KOBJ_ONLINE,8:KOBJ_OFFLINE,9:KOBJ_MAX10:};kobject_action定義了event的類型,包括:ADD/REMOVE,設備模型Kobject(或上層數據結構(gòu))的添加(jiā)/移除事件。
ONLINE/OFFLINE,Kobject(或上層(céng)數據結構)的上線/下線事件,其實是是否使能CHANGE,Kobject(或(huò)上層數據結(jié)構)的狀態或者內容發生改變MOVE,Kobject(或上層(céng)數(shù)據結構)更改名稱或者更改Parent(意味著在sysf設備模型s中更改了(le)目(mù)錄結構)。
CHANGE,如果設備(bèi)驅動需要(yào)上報的事件不再上麵事件的範圍(wéi)內,或者是自定義的事件(jiàn),可以使用該event,並攜帶相應的參數kobj_uevent_env1:/* include/linux/kobject.h, line 31 */。
2:#defineUEVENT_NUM_ENV設備(bèi)模型P32/* number of env pointers */3:#defineUEVENT_BUFFER_SIZE2048/* buffer for the variables */
4:5:/* include/linux/kobject.h, line 116 */6:structkobj_ue設備模型(xíng)vent_env{7:char*envp[UEVENT_NUM_ENVP];8:int
envp_idx;9:charbuf[UEVENT_BUFFER_SIZE];10:intbuflen;11:};前麵有(yǒu)提到(dào)過,在利用Kmod向用戶(hù)空間上報event事件時(shí),會直接(jiē)執(zhí)行用戶空間的可執(zhí)行文件(jiàn)。
而(ér)在Li設備模型nux係(xì)統,可執行文件的執行(háng),依賴於環境變量,因此kobj_uevent_env用於組織此次事件上(shàng)報時的環境變量envp,指針數組,用於(yú)保存每個環(huán)境變(biàn)量的地址,最多可支持的環(huán)境變量數量為UEVENT_NUM_ENVP。
envp_idx,用於(yú)訪問環境變量指針數組的indexbuf,保存環境變量的buf設備模型fer,最大為UEVENT_BUFFER_SIZEbuflen,訪問buf的(de)變量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量(liàng)身訂做的一個數據結構,設備模型裏麵包含filter和uevent兩個回調函數,用處如下:filter,當任何Kobject需要上報uevent時,它所屬的kset可以通過該接口過濾,阻(zǔ)止不希望上報的event,從而達到從整體上管理(lǐ)的目的。
name,該接口(kǒu)可以(yǐ)返回kset的名稱如(rú)果一個kset沒有合(hé)法的名稱,則其下的所有Kobje設備模型ct將不(bú)允許上報uventuevent,當任何Kobject需要上報uevent時,它所屬的kset可以通過該(gāi)接口統一為(wéi)這些event添加環境變量。
因為(wéi)很多時候上報uevent時的環境變量都是相(xiàng)同的,因此可以由kset統一處理,就不需要讓每個Kobject獨自添加了3.3 內部動作通過kobject設(shè)備模型.h,uevent模塊提供(gòng)了如下的(de)API(這些API的實現是在"lib/kobject_uevent.c”文件中):
1:/* include/linux/kobject.h, line 206 */2:intkobject_uevent(structkobject*kobj,enumkobject_設備(bèi)模型actionaction
);3:intkobject_uevent_env(structkobject*kobj,enumkobject_actionaction,4:char*envp[]);5:6:__printf(2,3
)7:intadd_uevent_var(structkobj_ueven設備模型t_env*env,constchar*format,...);8:9:intkobject_action_type(const
char*buf,size_tcount,10:enumkobject_action*type);kobject_uevent_env,以envp為環境變量,上報一個指定a設備(bèi)模型ction的uevent環境變量的作用是為執行用戶空(kōng)間(jiān)程序指定運(yùn)行環境。
具體動作如下:源代碼巨長(zhǎng),可以不看.../**
* kobject_uevent_env - send an uevent with environmental data
*
* 設(shè)備模型(xíng)@kobj: struct kobject that the action is happening to
* @action: action that is happening
* @envp_ext: pointer to environmental data
*
設(shè)備模型 * Returns 0 if kobject_uevent_env() is completed with success or the
* corresponding error when it fails.
*/intkobject_uevent_env(struc設備模型tkobject*kobj,enumkobject_actionaction
,char*envp_ext[]){structkobj_uevent_env*env;constchar*action_string=kobject_actions[action];constchar
*devpath=NU設備模型LL;constchar*subsystem;structkobject*top_kobj;structkset*kset;conststructkset_uevent_ops*uevent_ops
;inti=0;intretval=0;#ifdef CONFIG_NET
struc設備模型tuevent_sock*ue_sk;#endif
pr_debug("kobject: %s (%p): %s\n
",kobject_name(kobj),kobj,__func__);/* search the kset we belong to */top_kobj=kobj;設(shè)備模(mó)型while(!top_kobj->
kset&&top_kobj->parent)top_kobj=top_kobj->parent;if(!top_kobj->kset){pr_debug("kobject: %s (%p): %s: attempted to send uevent "
"witho設備模型ut kset!\n",kobject_name(kobj),kobj,__func__);return-EINVAL;}kset=top_kobj->kset;uevent_ops=kset
->uevent_ops;/* skip the event, if uevent_suppress is 設備模型set*/if(kobj->uevent_suppress){pr_debug("kobject: %s (%p): %s: uevent_suppress "
"caused the event to drop!\n",kobject_name(kobj),kobj,__func__);return設備模型0;}/* skip the event, if the filter returns zero. */
if(uevent_ops&&uevent_ops->filter)if(!uevent_ops->filter(kset,kobj)){pr_debug("kobject: %s (%p): %設備模型s: filter function "
"caused the event to drop!\n",kobject_name(kobj),kobj,__func__);return0;}/* originating subsystem */if
(uevent_ops&&uevent_ops->nam設(shè)備模型e)subsystem=uevent_ops->name(kset,kobj);elsesubsystem=kobject_name(&kset->
kobj);if(!subsystem){pr_debug("kobject: %s (%p): %s: unset subsystem caused 設備模型the ""event to drop!\n",kobject_name
(kobj),kobj,__func__);return0;}/* environment buffer */env=kzalloc(sizeof(structkobj_uevent_env),GFP_KERNEL
);if(!e設備模型nv)return-ENOMEM;/* complete object path */devpath=kobject_get_path(kobj,GFP_KERNEL);if(!devpath
){retval=-ENOENT;gotoexit;}/* default keys */retval=ad設備模(mó)型d_uevent_var(env,"ACTION=%s",action_string);if(
retval)gotoexit;retval=add_uevent_var(env,"DEVPATH=%s",devpath);if(retval)gotoexit;retval=add_uevent_va設備模型r
(env,"SUBSYSTEM=%s",subsystem);if(retval)gotoexit;/* keys passed in from the caller */if(envp_ext){for
(i=0;envp_ext[i];i++){retval=add_uevent_var(env設備模型,"%s",envp_ext[i]);if(retval)gotoexit;}}/* let the kset specific function add its stuff */
if(uevent_ops&&uevent_ops->uevent){retval=uevent_ops->uevent設備模型(kset,kobj,env);if(retval){pr_debug("kobject: %s (%p): %s: uevent() returned "
"%d\n",kobject_name(kobj),kobj,__func__,retval);gotoexit;}}switch(action設備模型){caseKOBJ_ADD:/*
* Mark "add" event so we can make sure we deliver "remove"
* event to userspace during automatic cleanup. If
* the ob設備(bèi)模型ject did send an "add" event, "remove" will
* automatically generated by the core, if not already done
* by the caller.
*/kobj->state_a設備模型dd_uevent_sent
=1;break;caseKOBJ_REMOVE:kobj->state_remove_uevent_sent=1;break;caseKOBJ_UNBIND:zap_modalias_env(env);
break;default:break;}mutex_lock(&u設備模型event_sock_mutex);/* we will send an event, so request a new sequence number */
retval=add_uevent_var(env,"SEQNUM=%llu",(unsignedlonglong)++uevent_seqn設備模型um);if(retval){mutex_unlock(&uevent_sock_mutex
);gotoexit;}#if defined(CONFIG_NET)
/* send netlink message */list_for_each_entry(ue_sk,&uevent_設備模型sock_list
,list){structsock*uevent_sock=ue_sk->sk;structsk_buff*skb;size_tlen;if(!netlink_has_listeners(uevent_sock
,1))continue;/* allocate message wit設(shè)備模型h the maximum possible size */len=strlen(action_string)+strlen(devpath
)+2;skb=alloc_skb(len+env->buflen,GFP_KERNEL);if(skb){char*scratch;/* add header設(shè)備模型 */scratch=skb_put(skb
,len);sprintf(scratch,"%s@%s",action_string,devpath);/* copy keys to our continuous event payload buffer */
for(i=0;ienvp_idx;i++設備模型){len=strlen(env->envp[i])+1;scratch=skb_put(skb,len);strcpy(scratch,env->
envp[i]);}NETLINK_CB(skb).dst_group=1;retval=netlink_broadcast_filtered(ueve設備模型nt_sock,skb,0,1,GFP_KERNEL
,kobj_bcast_filter,kobj);/* ENOBUFS should be handled in userspace */if(retval==-ENOBUFS||retval==-ESRCH
)retval=0;}elseretva設備模型l=-ENOMEM;}#endif
mutex_unlock(&uevent_sock_mutex);#ifdef CONFIG_UEVENT_HELPER
/* call uevent_helper, usually only enabled during earl設備模型y boot */
if(uevent_helper[0]&&!kobj_usermode_filter(kobj)){structsubprocess_info*info;retval=add_uevent_var(env
,"HOME=/");if(retval)gotoexit;retval=ad設備模型d_uevent_var(env,"PATH=/sbin:/bin:/usr/sbin:/usr/bin");if(retval
)gotoexit;retval=init_uevent_argv(env,subsystem);if(retval)gotoexit;retval=-ENOMEM;inf設備模型o=call_usermodehelper_setup
(env->argv[0],env->argv,env->envp,GFP_KERNEL,NULL,cleanup_uevent_env,env);if(info){retval=call_usermodehelper_exec
(info,UMH設備模型_NO_WAIT);env=NULL;/* freed by cleanup_uevent_env */}}#endif
exit:kfree(devpath);kfree(env);
returnretval;}EXPORT_SYMBOL_GPL(kobject_uevent_env設備模(mó)型);查找kobj本身或者其(qí)parent是否從屬於某(mǒu)個kset,如果不是(shì),則報錯返回(注2:由此可以說明,如果一個kobject沒有加入kset,是不(bú)允許(xǔ)上報uevent的)
查(chá)看kobj->uevent_suppress是否設置(zhì),如果(guǒ)設置,則忽略所有的uevent上報並返回(注3:由此(cǐ)可知,可以通過K設備模型object的uevent_suppress標(biāo)誌,管控Kobject的uevent的上報)
如果所屬的kset有uevent_ops->filter函數,則調用該函數,過(guò)濾此次上報(注4:這佐證了3.2小節有關filter接口的說明,kset可以通過(guò)filter接口過濾不希望上報的event,從而達(dá)到設備(bèi)模型整體的管理效果)
判斷(duàn)所屬的kset是否有合法的名稱(稱作subsystem,和前期的(de)內核版本有(yǒu)區別),否則不(bú)允許上報(bào)uevent分配一個用於此次上(shàng)報的、存儲(chǔ)環境變量的(de)buffer(結果保存在env指針中),並(bìng)獲得該Kobject在sysfs中路(lù)徑信息(用(yòng)戶空間軟件需要依據該路徑信息在sysfs中訪問設(shè)備模型它)
調用(yòng)add_uevent_var接口(下麵會(huì)介紹),將Action、路(lù)徑信息、subsystem等(děng)信息(xī),添加到env指針(zhēn)中如果傳入的envp不空,則解析傳入的環境變量中,同樣調用add_uevent_var接口,添加到env指針中
如果(guǒ)所屬的kset存在(zài)uevent_ops->uevent接口,調設備模型用該接口,添加kset統一的環境變量到env指(zhǐ)針根(gēn)據ACTION的類型,設置kobj->state_add_uevent_sent和kobj->state_remove_uevent_sent變量,以記錄正確的狀態
調用add_uevent_var接口(kǒu),添加格式為"SEQNUM=%llu”的(de)序列(liè)號如果設備模型(xíng)定義了"CONFIG_NET”,則使用netlink發送該uevent以uevent_helper、subsystem以及添加了(le)標準環境變量(HOME=/,PATH=/sbin:/bin:/usr/sbin:/usr/bin)的env指針為(wéi)參數,調用kmod模塊提供的call_usermodehel設備模型per函數,上報uevent。
其中uevent_helper的內容是由內核配置項CONFIG_UEVENT_HELPER_PATH(位於./drivers/base/Kconfig)決定的(可參考lib/kobject_uevent.c, line 32),該配置項指定了一個用戶(hù)空間程序(或者腳本)設備模(mó)型,用於解(jiě)析上報的uevent,例如"/sbin/hotplug”。
call_usermodehelper的作用,就是fork一個進程,以uevent為參數(shù),執行uevent_helperkobject_uevent,和kobject_uevent_env功能一樣,隻是沒有(yǒu)指定任何的環境變量。
/**
設備模型 * kobject_uevent - notify userspace by sending an uevent
*
* @kobj: struct kobject that the action is happening to
* @action: a設備模型ction that is happening
*
* Returns 0 if kobject_uevent() is completed with success or the
* corresponding error when it fails.
設備模型 */intkobject_uevent(structkobject*kobj,enumkobject_actionaction
){returnkobject_uevent_env(kobj,action,NULL);}EXPORT_SYMBOL_GPL(kobject_uevent);add_ue設(shè)備模型vent_var,以格式化字符的形式(類似printf、printk等),將環境變量copy到env指針中。
/**
* add_uevent_var - add key value string to the environment buffer
* @env: en設備模(mó)型vironment buffer structure
* @format: printf format for the key=value pair
*
* Returns 0 if environment variable was added successfully設備模型 or -ENOMEM
* if no space was available.
*/intadd_uevent_var(structkobj_uevent_env*env,constchar*format,...){va_list
args;intlen;if(env->envp_id設備模型x>=ARRAY_SIZE(env->envp)){WARN(1,KERN_ERR"add_uevent_var: too many keys\n"
);return-ENOMEM;}va_start(args,format);len=vsnprintf(&env->buf[env->buflen],設備模型sizeof(env->buf)-env->buflen
,format,args);va_end(args);if(len>=(sizeof(env->buf)-env->buflen)){WARN(1,KERN_ERR"add_uevent_var: buffer size too small
\n設備模型");return-ENOMEM;}env->envp[env->envp_idx++]=&env->buf[env->buflen];env->buflen+=len+1;return0;}EXPORT_SYMBOL_GPL
(add_uevent_var);kobject_action_type,設備模型將enum kobject_action類型的Action,轉換為字符串staticintkobject_action_type。
(constchar*buf,size_tcount,enumkobject_action*type,constchar**args){enumkobject_actio設備模型naction;size_tcount_first
;constchar*args_start;intret=-EINVAL;if(count&&(buf[count-1]==\n||buf[count-1]==\0))count--;if(!count
)gotoout;args_start=strn設(shè)備模型chr(buf,count,);if(args_start){count_first=args_start-buf;args_start=args_start
+1;}elsecount_first=count;for(action=0;action
[action],buf,count_first)!設備模型=0)continue;if(kobject_actions[action][count_first]!=\0)continue;if(args)*args
=args_start;*type=action;ret=0;break;}out:returnret;}怎麽指定處理uevent的用戶(hù)空間程序設備模型(簡稱uevent helper)?上(shàng)麵介紹kobject_uevent_env的內部動作時,有提到,Uevent模塊(kuài)通過Kmod上報Uevent時,會通過call_usermodehelper函數,調用(yòng)用戶空間的可(kě)執行文件(或者(zhě)腳本,簡稱uevent helper)處理該event。
而該ueven設備模(mó)型(xíng)t helper的路徑保存在uevent_helper數組中(zhōng)可以在編譯內核(hé)時,通過CONFIG_UEVENT_HELPER_PATH配置項,靜態指定(dìng)uevent helper但這種方式會為每個event fork一個進程,隨著(zhe)內核支持的設備數量的增多,這種方式在(zài)係統啟動時(shí)將會是致命的(可以導致內存溢設備模型(xíng)出等(děng))。
因此隻(zhī)有在早期的內(nèi)核版本中會使用這種方式,現在內核(hé)不再推薦使用該(gāi)方式(shì)因此內核編譯時,需要把該配置項留空在係統啟動後,大部分的設備已經ready,可以根據需要(yào),重新指定一個uevent helper,以便檢測係統運行過程中的熱拔插事件。
這可以通過把helper的路徑寫入到"/sys/kerne設備模型l/uevent_helper”文件中實現實際上,內(nèi)核(hé)通過sysfs文件(jiàn)係統的(de)形式,將uevent_helper數組開放到用戶(hù)空間,供用戶空間程序(xù)修改訪問,具體可參考"./kernel/ksysfs.c”中相應的代碼,這裏不再詳細描述。
轉載者注:uevent helper這部分我也沒有(yǒu)理解,等明(míng)白了設備模型,稍後(hòu)補(bǔ)上(shàng),也可以去原文谘詢原作者。
歡迎(yíng)大(dà)家關注我的微(wēi)信公(gōng)眾號——小(xiǎo)白倉庫(kù) 原創經驗資料分享:包含但不僅限於FPGA、ARM、RISC-V、Linux、LabVIEW等軟(ruǎn)硬件開(kāi)發,另外分享生活(huó)中的(de)趣事以及感悟目的是建立一(yī)個平(píng)台記錄學習過的知識,並分享出來自認為有用的與感興趣的道友相互交(jiāo)流進步。
Copyright © 2002-2020 上海潤之模型設計有限公司 版權(quán)所有 展示模型,展品模(mó)型,展廳模型,展示道具,展廳(tīng)展品(pǐn),展品道具,模型定(dìng)製,模型公司,上海模型公司 備案號:滬ICP備20018260號