版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
本文格式為Word版,下載可任意編輯——linux設(shè)備模型深探一:前言
Linux設(shè)備模型是一個(gè)極其繁雜的結(jié)構(gòu)體系,在編寫驅(qū)動(dòng)程序的時(shí)候,尋常不會(huì)用到這方面的東西,但是。理解這部份內(nèi)容,對(duì)于我們理解linux設(shè)備驅(qū)動(dòng)的結(jié)構(gòu)是大有裨益的。我們不但可以在編寫程序程序的時(shí)候知其然,亦知其所以然。又可以學(xué)習(xí)到一種極其精致的架構(gòu)設(shè)計(jì)方法。由于之前已經(jīng)詳細(xì)分析了sysfs文件系統(tǒng)。所以本節(jié)的探討主要集中在設(shè)備模型的底層實(shí)現(xiàn)上。上層的接口,如pci.,usb,網(wǎng)絡(luò)設(shè)備都可以看成是底層的封裝。
二:kobject,kset和ktype
Kobject,kset,kypte這三個(gè)結(jié)構(gòu)是設(shè)備模型中的下層架構(gòu)。模型中的每一個(gè)元素都對(duì)應(yīng)一個(gè)kobject.kset和ktype可以看成是kobject在層次結(jié)構(gòu)與屬性結(jié)構(gòu)方面的擴(kuò)展。將三者之間的關(guān)系用圖的方示描述如下:
如上圖所示:我們知道。在sysfs中每一個(gè)目錄都對(duì)應(yīng)一個(gè)kobject.這些kobject都有自己的parent。在沒(méi)有指定parent的狀況下,都會(huì)指向它所屬的kset->object。其次,kset也內(nèi)嵌了kobject.這個(gè)kobject又可以指它上一級(jí)的parent。就這樣。構(gòu)成了一個(gè)空間上面的層次關(guān)系。
其實(shí),每個(gè)對(duì)象都有屬性。例如,電源管理,執(zhí)插撥事性管理等等。由于大部份的同類設(shè)備都有一致的屬性,因此將這個(gè)屬性隔離開(kāi)來(lái),存放在ktype中。這樣就可以靈活的管理了.記得在分析sysfs的時(shí)候。對(duì)于sysfs中的普通文件讀寫操作都是由kobject->ktype->sysfs_ops來(lái)完成的.
經(jīng)過(guò)上面的分析,我們大約了解了kobject.kset與ktype的大約架設(shè)與相互之間的關(guān)系。下面我們從linux源代碼中的分析來(lái)詳細(xì)研究他們的操作。
三:kobject,kset和ktype的操作
為了說(shuō)明kobject的操作,先寫一個(gè)測(cè)試模塊,代碼如下:
#include
#include
#include
#include
#include
#include
#include
MODULE_AUTHOR(\
MODULE_LICENSE(\
voidobj_test_release(structkobject*kobject);
ssize_teric_test_show(structkobject*kobject,structattribute*attr,char*buf);
ssize_teric_test_store(structkobject*kobject,structattribute*attr,constchar*buf,size_tcount);
structattributetest_attr={
.name=\
.mode=S_IRWXUGO,};
staticstructattribute*def_attrs[]={
structsysfs_opsobj_test_sysops={
.show=eric_test_show,
.store=eric_test_store,};
structkobj_typektype={
.release=obj_test_release,
.sysfs_ops=
voidobj_test_release(structkobject*kobject){
printk(\}
ssize_teric_test_show(structkobject*kobject,structattribute*attr,char*buf){
printk(\
printk(\
sprintf(buf,\
returnstrlen(attr->name)+2;}
ssize_teric_test_store(structkobject*kobject,structattribute*attr,constchar*buf,size_tcount){
printk(\
printk(\
returncount;}
structkobjectkobj;
staticintkobject_test_init(){
printk(\
kobject_init_and_add(}
staticintkobject_test_exit(){
printk(\
kobject_del(
return0;}
module_init(kobject_test_init);
module_exit(kobject_test_exit);
加載模塊之后,會(huì)發(fā)現(xiàn),在/sys下多了一個(gè)eric_test目錄。該目錄下有一個(gè)叫eric_xiao的文件。如下所示:
[root@localhosteric_test]#ls
eric_xiao
用cat觀測(cè)此文件:
[root@localhosteric_test]#cateric_xiao
eric_xiao
再用echo往里面寫點(diǎn)東西;
[root@localhosteric_test]#echohello>eric_xiao
Dmesg的輸出如下:
haveshow.
attrname:eric_xiao.
havestore
write:hello
如上所示。我們看到了kobject的大約建立過(guò)程.我們來(lái)看一下kobject_init_and_add()的實(shí)現(xiàn)。在這個(gè)函數(shù)里,包含了對(duì)kobject的大部份操作。
intkobject_init_and_add(structkobject*kobj,structkobj_type*ktype,
structkobject*parent,constchar*fmt,...){
va_listargs;
intretval;
//初始化kobject
kobject_init(kobj,ktype);
va_start(args,fmt);
//為kobjcet設(shè)置名稱,在sysfs中建立相關(guān)信息
retval=kobject_add_varg(kobj,parent,fmt,args);
va_end(args);
returnretval;}
上面的流程主要分為兩部份。一部份是kobject的初始化。在這一部份,它將kobject與給定的ktype關(guān)聯(lián)起來(lái)。初始化kobject中的各項(xiàng)結(jié)構(gòu)。另一部份是kobject的名稱設(shè)置??臻g層次關(guān)系的設(shè)置,具體表現(xiàn)在sysfs文件系統(tǒng)中.
對(duì)于第一部份,代碼比較簡(jiǎn)單,這里不再贅述。跟蹤其次部份,也就是kobject_add_varg()的實(shí)現(xiàn).
staticintkobject_add_varg(structkobject*kobj,structkobject*parent,
constchar*fmt,va_listvargs){
va_listaq;
intretval;
va_copy(aq,vargs);
//設(shè)置kobject的名字。即kobject的name成員
retval=kobject_set_name_vargs(kobj,fmt,aq);
va_end(aq);
if(retval){
printk(KERN_ERR\
returnretval;
}
//設(shè)置kobject的parent。在上面的例子中,我們沒(méi)有給它指定父結(jié)點(diǎn)
kobj->parent=parent;
//在sysfs中添加kobjcet信息
returnkobject_add_internal(kobj);}
設(shè)置好kobject->name后,轉(zhuǎn)入kobject_add_internal()。在sysfs中創(chuàng)立空間結(jié)構(gòu).代碼如下:
staticintkobject_add_internal(structkobject*kobj){
interror=0;
structkobject*parent;
if(!kobj)
return-ENOENT;
//假使kobject的名字為空.退出
if(!kobj->name||!kobj->name[0]){
pr_debug(\
\
WARN_ON(1);
return-EINVAL;
}
//取kobject的父結(jié)點(diǎn)
parent=kobject_get(kobj->parent);
//假使kobject的父結(jié)點(diǎn)沒(méi)有指定,就將kset->kobject做為它的父結(jié)點(diǎn)
/*joinksetifset,useitasparentifwedonotalreadyhaveone*/
if(kobj->kset){
if(!parent)
parent=kobject_get(
kobj_kset_join(kobj);
kobj->parent=parent;
}
//調(diào)試用
pr_debug(\
kobject_name(kobj),kobj,__FUNCTION__,
parent?kobject_name(parent):\
kobj->kset?kobject_name(
if(error){
//v假使創(chuàng)立失敗。減少相關(guān)的引用計(jì)數(shù)
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent=NULL;
/*benoisyonerrorissues*/
if(error==-EEXIST)
printk(KERN_ERR\\\__FUNCTION__,kobject_name(kobj));
else
printk(KERN_ERR\
__FUNCTION__,kobject_name(kobj),error);
dump_stack();
}else
//假使創(chuàng)立成功。將state_in_sysfs建為1。表示該object已經(jīng)在sysfs中了
kobj->state_in_sysfs=1;
returnerror;}
這段代碼比較簡(jiǎn)單,它主要完成kobject父結(jié)點(diǎn)的判斷和選定,然后再調(diào)用create_dir()在sysfs創(chuàng)立相關(guān)信息。該函數(shù)代碼如下:
staticintcreate_dir(structkobject*kobj){
interror=0;
if(kobject_name(kobj)){
//為kobject創(chuàng)立目錄
error=sysfs_create_dir(kobj);
if(!error){
//為kobject->ktype中的屬性創(chuàng)立文件
error=populate_dir(kobj);
if(error)sysfs_remove_dir(kobj);
}
}
returnerror;}
我們?cè)谏厦娴睦缰锌吹降?sys下的eric_test目錄,以及該目錄下面的eric_xiao的這個(gè)文件就是這里被創(chuàng)立的。我們先看一下kobject所表示的目錄創(chuàng)立過(guò)程。這是在sysfs_create_dir()中完成的。代碼如下:
intsysfs_create_dir(structkobject*kobj){
structsysfs_dirent*parent_sd,*sd;
interror=0;
BUG_ON(!kobj);
/*假使kobject的parnet存在。就在目錄點(diǎn)的目錄下創(chuàng)立這個(gè)目錄。假使沒(méi)有父結(jié)點(diǎn)不存在,就在/sys下面創(chuàng)立結(jié)點(diǎn)。在上面的流程中,我們可能并沒(méi)有為其指定父結(jié)點(diǎn),也沒(méi)有為其指定kset。*/
if(kobj->parent)
parent_sd=kobj->parent->sd;
else
parent_sd=
//在sysfs中創(chuàng)立目錄
error=create_dir(kobj,parent_sd,kobject_name(kobj),
if(!error)
kobj->sd=sd;
returnerror;}
在這里,我們就要聯(lián)系之前分析過(guò)的sysfs文件系統(tǒng)的研究了。假使不太明白的,可以在找到那篇文章細(xì)心的研讀一下。create_dir()就是在sysfs中創(chuàng)立目錄的接口,在之前已經(jīng)詳細(xì)分析過(guò)了。這里不再陳述。
接著看為kobject->ktype中的屬性創(chuàng)立文件。這是在populate_dir()中完成的。代碼如下:
staticintpopulate_dir(structkobject*kobj){
structkobj_type*t=get_ktype(kobj);
structattribute*attr;
interror=0;
inti;
if(t(attr=t->default_attrs[i])!=NULL;i++){
error=sysfs_create_file(kobj,attr);
if(error)break;
}
}
returnerror;}
這段代碼比較簡(jiǎn)單。它遍歷ktype中的屬性。然后為其建立文件。請(qǐng)注意:文件的操作最終都會(huì)回溯到ktype->sysfs_ops的show和store這兩個(gè)函數(shù)中.
Kobject的創(chuàng)立已經(jīng)分析完了,接著分析怎么將一個(gè)kobject注銷掉。注意過(guò)程是在kobject_del()中完成的。代碼如下:
voidkobject_del(structkobject*kobj){
if(!kobj)
return;
sysfs_remove_dir(kobj);
kobj->state_in_sysfs=0;
kobj_kset_leave(kobj);
kobject_put(kobj->parent);
kobj->parent=NULL;}
該函數(shù)會(huì)將在sysfs中的kobject對(duì)應(yīng)的目錄刪除。請(qǐng)注意,屬性文件是建立在這個(gè)目錄下面的。只需要將這個(gè)目錄刪除。屬性文件也隨之刪除。
是后,減少相關(guān)的引用計(jì)數(shù),假使kobject的引用計(jì)數(shù)為零。則將其所占空間釋放.
Kset的操作與kobject類似,由于kset中內(nèi)嵌了一個(gè)kobject結(jié)構(gòu),所以,大部份操作都是集中在kset->kobject上.具體分析一下kset_create_and_add()這個(gè)接口,類似上面分析的kobject接口,這個(gè)接口也包括了kset的大部分操作.代碼如下:
structkset*kset_create_and_add(constchar*name,structkset_uevent_ops*uevent_ops,structkobject*parent_kobj){
structkset*kset;
interror;
//創(chuàng)立一個(gè)kset
kset=kset_create(name,uevent_ops,parent_kobj);
if(!kset)
returnNULL;
//注冊(cè)kset
error=kset_register(kset);
if(error){
//假使注冊(cè)失敗,釋放kset
kfree(kset);
returnNULL;
}
returnkset;}
Kset_create()用來(lái)創(chuàng)立一個(gè)structkset結(jié)構(gòu).代碼如下:
staticstructkset*kset_create(constchar*name,structkset_uevent_ops*uevent_ops,structkobject*parent_kobj){
structkset*kset;
kset=kzalloc(sizeof(*kset),GFP_KERNEL);
if(!kset)
returnNULL;
kobject_set_name(
kset->uevent_ops=uevent_ops;
kset->kobj.parent=parent_kobj;
kset->kobj.ktype=
kset->kobj.kset=NULL;
returnkset;}
我們注意,在這里創(chuàng)立kset時(shí).為其內(nèi)嵌的kobject指定其ktype結(jié)構(gòu)為kset_ktype.這個(gè)結(jié)構(gòu)的定義如下:
staticstructkobj_typekset_ktype={
.sysfs_ops=
屬性文件的讀寫操作全部都包含在sysfs_ops成員里.kobj_sysfs_ops的定義如下:
structsysfs_opskobj_sysfs_ops={
.show=kobj_attr_show,
.store=kobj_attr_store,};
Show,store成員對(duì)應(yīng)的函數(shù)代碼如下所示:
staticssize_tkobj_attr_show(structkobject*kobj,structattribute*attr,char*buf){
structkobj_attribute*kattr;
ssize_tret=-EIO;
kattr=container_of(attr,structkobj_attribute,attr);
if(kattr->show)
ret=kattr->show(kobj,kattr,buf);
returnret;}
staticssize_tkobj_attr_store(structkobject*kobj,structattribute*attr,constchar*buf,size_tcount){
structkobj_attribute*kattr;
ssize_tret=-EIO;
kattr=container_of(attr,structkobj_attribute,attr);
if(kattr->store)
ret=kattr->store(kobj,kattr,buf,count);
returnret;}
從上面的代碼看以看出.會(huì)將structattribute結(jié)構(gòu)轉(zhuǎn)換為structkobj_attribte結(jié)構(gòu).也就是說(shuō)structkobj_attribte內(nèi)嵌了一個(gè)structattribute.實(shí)際上,這是和宏__ATTR協(xié)同在一起使用的.經(jīng)常用于group中.在這里并不計(jì)劃研究group.原理都是一樣的.這里列出來(lái)只是做個(gè)說(shuō)明而已.
創(chuàng)立好了kset之后,會(huì)調(diào)用kset_register().這個(gè)函數(shù)就是kset操作的核心代碼了.如下:
intkset_register(structkset*k){
interr;
if(!k)
return-EINVAL;
kset_init(k);
err=kobject_add_internal(
if(err)
returnerr;
kobject_uevent(
return0;}
在kset_init()里會(huì)初始化kset中的其它字段.然后調(diào)用kobject_add_internal()為其內(nèi)嵌的
kobject結(jié)構(gòu)建立空間層次結(jié)構(gòu).之后由于添加了kset.會(huì)產(chǎn)生一個(gè)事件.這個(gè)事件是通過(guò)用戶空間的hotplug程序處理的.這就是kset明顯不同于kobject的地方.詳細(xì)研究一下這個(gè)函數(shù).這對(duì)于我們研究hotplug的深層機(jī)理是很有幫助的.它的代碼如下;
intkobject_uevent(structkobject*kobj,enumkobject_actionaction){
returnkobject_uevent_env(kobj,action,NULL);}
之后,會(huì)調(diào)用kobject_uevent_env().這個(gè)函數(shù)中的三個(gè)參數(shù)含義分別為:引起事件的kobject.事件類型(add,remove,change,move,online,offline等).第三個(gè)參數(shù)是要添加的環(huán)境變量.
代碼篇幅較長(zhǎng),我們效仿情景分析上面的做法.分段分析如下:
intkobject_uevent_env(structkobject*kobj,enumkobject_actionaction,char*envp_ext[]){
structkobj_uevent_env*env;
constchar*action_string=kobject_actions[action];
constchar*devpath=NULL;
constchar*subsystem;
structkobject*top_kobj;
structkset*kset;
structkset_uevent_ops*uevent_ops;
u64seq;
inti=0;
intretval=0;
pr_debug(\
kobject_name(kobj),kobj,__FUNCTION__);
/*searchtheksetwebelongto*/
top_kobj=kobj;
while(!top_kobj->kset
if(!top_kobj->kset){
pr_debug(\
\
__FUNCTION__);
return-EINVAL;
}
由于對(duì)事件的處理函數(shù)包含在kobject->kset->uevent_ops中.要處理事件,就必需要找到上層的一個(gè)不為空的kset.上面的代碼就是順著kobject->parent找不到一個(gè)不為空的kset.假使不存在這樣的kset.就退出
kset=top_kobj->kset;
uevent_ops=kset->uevent_ops;
/*skiptheevent,ifthefilterreturnszero.*/
if(uevent_ops
return0;
}
/*originatingsubsystem*/
if(uevent_ops
else
subsystem=kobject_name(
if(!subsystem){
pr_debug(\
\
__FUNCTION__);
return0;
}
找到了不為空的kset.就跟kset->uevent_ops->filter()匹配.看這個(gè)事件是否被過(guò)濾.假使沒(méi)有被過(guò)濾掉.就會(huì)調(diào)用kset->uevent_ops->name()得到子系統(tǒng)的名稱,假使不存在kset->uevent_ops->name().就會(huì)以kobject->name做為子系統(tǒng)名稱.
/*environmentbuffer*/
env=kzalloc(sizeof(structkobj_uevent_env),GFP_KERNEL);
if(!env)
return-ENOMEM;
/*completeobjectpath*/
devpath=kobject_get_path(kobj,GFP_KERNEL);
if(!devpath){
retval=-ENOENT;
gotoexit;
}
/*defaultkeys*/
retval=add_uevent_var(env,\
if(retval)
gotoexit;
retval=add_uevent_var(env,\
if(retval)
gotoexit;
retval=add_uevent_var(env,\
if(retval)
gotoexit;
/*keyspassedinfromthecaller*/
if(envp_ext){
for(i=0;envp_ext[i];i++){
retval=add_uevent_var(env,envp_ext[i]);
if(retval)gotoexit;
}
}
接下來(lái),就應(yīng)當(dāng)設(shè)置為調(diào)用hotplug設(shè)置環(huán)境變量了.首先,分派一個(gè)structkobj_uevent_env結(jié)構(gòu)用來(lái)存放環(huán)境變量的值.然后調(diào)用kobject_get_path()用來(lái)獲得引起事件的kobject在sysfs中的路徑.再調(diào)用add_uevent_var()將動(dòng)作代表的字串,kobject路徑,子系統(tǒng)名稱填充到structkobj_uevent_env中,假使有指定環(huán)境變量,也將其添加進(jìn)去.kobject_get_path()和add_uevent_var()都比較簡(jiǎn)單.這里不再詳細(xì)分析了.請(qǐng)自行查看源代碼
/*lettheksetspecificfunctionadditsstuff*/
if(uevent_ops
if(retval){
pr_debug(\\__FUNCTION__,retval);
gotoexit;
}
}
/*
*Mark\
*eventstouserspaceduringautomaticcleanup.Iftheobjectdid
*sendan\
*thecore,ifnotalreadydonebythecaller.
*/
if(action==KOBJ_ADD)
kobj->state_add_uevent_sent=1;
elseif(action==KOBJ_REMOVE)
kobj->state_remove_uevent_sent=1;
/*wewillsendanevent,sorequestanewsequencenumber*/
spin_lock(
seq=++uevent_seqnum;
spin_unlock(
retval=add_uevent_var(env,\
if(retval)
gotoexit;
在這里還會(huì)調(diào)用kobject->kset->uevent_ops->uevent().讓產(chǎn)生事件的kobject添加環(huán)境變量.最
后將事件序列添加到環(huán)境變量中去.
#ifdefined(CONFIG_NET)
/*sendnetlinkmessage*/
if(uevent_sock){
structsk_buff*skb;
size_tlen;
/*allocatemessagewiththemaximumpossiblesize*/
len=strlen(action_string)+strlen(devpath)+2;
skb=alloc_skb(len+env->buflen,GFP_KERNEL);
if(skb){
char*scratch;
/*addheader*/
scratch=skb_put(skb,len);
sprintf(scratch,\
/*copykeystoourcontinuouseventpayloadbuffer*/
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;
netlink_broadcast(uevent_sock,skb,0,1,GFP_KERNEL);
}
}
#endif
/*calluevent_helper,usuallyonlyenabledduringearlyboot*/
if(uevent_helper[0]){
char*argv[3];
argv[0]=uevent_helper;
argv[1]=(char*)subsystem;
argv[2]=NULL;
retval=add_uevent_var(env,\
if(retval)
gotoexit;
retval=add_uevent_var(env,
\
if(retval)
gotoexit;
call_usermodehelper(argv[0],argv,env->envp,UMH_WAIT_EXEC);
}exit:
kfree(devpath);
kfree(env);
returnretval;}
忽略一段選擇編譯的代碼.再后就是調(diào)用用戶空間的hotplug了.添加最終兩個(gè)環(huán)境變量.HOME和PATH.然后調(diào)用hotplug.以子系統(tǒng)名稱為參數(shù).
現(xiàn)在我們終究知道hotplug處理程序中的參數(shù)和環(huán)境變量是怎么來(lái)的了.^_^
使用完了kset.再調(diào)用kset_unregister()將其注銷.這個(gè)函數(shù)很簡(jiǎn)單,請(qǐng)自行查閱代碼.
為了印證一下上面的分析,寫一個(gè)測(cè)試模塊。如下:
#include
#include
#include
#include
#include
#include
#include
#include
MODULE_AUTHOR(\
MODULE_LICENSE(\
intkset_filter(structkset*kset,structkobject*kobj);
constchar*kset_name(structkset*kset,structkobject*kobj);
intkset_uevent(structkset*kset,structkobject*kobj,
structkobj_uevent_env*env);
structksetkset_p;
structksetkset_c;
structkset_uevent_opsuevent_ops={
.filter=kset_filter,
.name=kset_name,
.uevent=kset_uevent,};
intkset_filter(structkset*kset,structkobject*kobj){
printk(\
return1;}
constchar*kset_name(structkset*kset,structkobject*kobj){
staticcharbuf[20];
printk(\
sprintf(buf,\
returnbuf;}
intkset_uevent(structkset*kset,structkobject*kobj,
structkobj_uevent_env*env){
inti=0;
printk(\
while(ienvp_idx){printk(\i++;
}
return0;}
intkset_test_init(){
printk(\
kobject_set_name(
kset_register(
kobject_set_name(
kset_register(
return0;}
intkset_test_exit(){
printk(\
kset_unregister(
kset_unregister(
return0;}
module_init(kset_test_init);
module_exit(kset_test_exit);
在這里,定義并注冊(cè)了二個(gè)kset.其次個(gè)kset的kobj->kset域指向第一個(gè)kset.這樣,當(dāng)其次個(gè)kset注冊(cè)或者卸載的時(shí)候就會(huì)調(diào)用第一個(gè)kset中的uevent_ops的相關(guān)操作.
kset_p.uevent_ops->filter函數(shù)中,使其返回1.使其匹配成功。
在kset_p.uevent_ops->name中。使其返回的子系統(tǒng)名為引起事件的kobject的名稱,即:kset_c.
最終在kset_p.uevent_ops->uevent中將環(huán)境變量全部打印出來(lái)。
下面是dmesg的輸出結(jié)果:
ksettestinit.
UEVENT:filter.kobjkset_c.
UEVENT:name.kobjkset_c.
UEVENT:uevent.kobjkset_c.
ACTION=add.
DEVPATH=/kset_p/kset_c.
SUBSYSTEM=kset_test.
輸出結(jié)果跟我們的分析是吻合的.
在這里,值得我們注意的是。注冊(cè)一個(gè)kobject不會(huì)產(chǎn)生事件,只有注冊(cè)kset才會(huì).
四:bus,device和device_driver
上面分析了kobject.kset,ktype.這三個(gè)結(jié)構(gòu)聯(lián)合起來(lái)一起構(gòu)成了整個(gè)設(shè)備模型的基石.而bus.device.device_driver.則是基于kobject.kset.ktype之上的架構(gòu).在這里,總線,設(shè)備,驅(qū)動(dòng)被有序的組和在一起.
Bus.device.device_driver三者之間的關(guān)系如下圖所示:
如上圖所示.structbus_type的p->drivers_kset指向注冊(cè)在上面的驅(qū)動(dòng)程序.它的p->device_kset上掛著注冊(cè)在上面的設(shè)備.每次有一個(gè)新的設(shè)備注冊(cè)到上面,都會(huì)去匹配右邊的驅(qū)動(dòng),看是否能匹配上.假使匹配成功,則將設(shè)備結(jié)構(gòu)的is_registerd域置為0.然后將設(shè)備添加到驅(qū)動(dòng)的p->klist_devices域.同理,每注冊(cè)一個(gè)驅(qū)動(dòng),都會(huì)去匹配左邊的設(shè)備,.假使匹配成功,將則設(shè)備加到驅(qū)動(dòng)的p->klist_devices域.再將設(shè)備的is_registerd置為0/
這就是linux設(shè)備模型用來(lái)管理設(shè)備和驅(qū)動(dòng)的基本架構(gòu).我們來(lái)跟蹤一下代碼來(lái)看下詳細(xì)的操作.
注冊(cè)一個(gè)總線的接口為bus_register().我們循例分段分析:
intbus_register(structbus_type*bus){
intretval;
structbus_type_private*priv;
priv=kzalloc(sizeof(structbus_type_private),GFP_KERNEL);
if(!priv)
return-ENOMEM;
priv->bus=bus;
bus->p=priv;
BLOCKING_INIT_NOTIFIER_HEAD(
retval=kobject_set_name(
priv->subsys.kobj.kset=bus_kset;
priv->subsys.kobj.ktype=
priv->drivers_autoprobe=1;
retval=kset_register(
if(retval)
gotoout;
首先,先為structbus_type的私有區(qū)分派空間,然后將其和structbus_type關(guān)聯(lián)起來(lái).由于structbus_type也要在sysfs文件中表示一個(gè)節(jié)點(diǎn),因此,它也內(nèi)嵌也一個(gè)kset的結(jié)構(gòu).這就是priv->subsys.
首先,它為這個(gè)kset的名稱賦值為bus的名稱,然后將priv->subsys.kobj.kset指向bus_kset.priv->subsys.kobj.ktype指向bus_ktype;然后調(diào)用kset_reqister()將priv->subsys注冊(cè).這里涉及到的接口都在之前分析過(guò).注冊(cè)過(guò)后,應(yīng)當(dāng)會(huì)在bus_kset所表示的目錄下創(chuàng)立一個(gè)總線名稱的目錄.并且用戶空間的hotplug應(yīng)當(dāng)會(huì)檢測(cè)到一個(gè)add事件.我們來(lái)看一下bus_kset終究指向的是什么:
bus_kset=kset_create_and_add(\
此后可以看出.這個(gè)bus_kset在sysfs中的結(jié)點(diǎn)就是/sys/bus.在這里注冊(cè)的structbus_types就
會(huì)在/sys/bus/下面出現(xiàn).
retval=bus_create_file(bus,
if(retval)
gotobus_uevent_fail;
bus_create_file()就是在priv->subsys.kobj的這個(gè)kobject上建立一個(gè)普通屬性的文件.這個(gè)文件的屬性對(duì)應(yīng)在bus_attr_uevent.讀寫操作對(duì)應(yīng)在priv->subsys.ktype中.我們到后面才統(tǒng)一分析bus注冊(cè)時(shí)候的文件創(chuàng)立
priv->devices_kset=kset_create_and_add(\
if(!priv->devices_kset){
retval=-ENOMEM;
gotobus_devices_fail;
}
priv->drivers_kset=kset_create_and_add(\
if(!priv->drivers_kset){
retval=-ENOMEM;
gotobus_drivers_fail;
}
klist_init(
klist_init(
這段代碼會(huì)在bus所在的目錄下建立兩個(gè)目錄,分別為devices和drivers.并初始化掛載設(shè)備和驅(qū)動(dòng)的鏈表
retval=add_probe_files(bus);
if(retval)
gotobus_probe_files_fail;
retval=bus_add_attrs(bus);
if(retval)
gotobus_attrs_fail;
pr_debug(\
return0;
在這里,會(huì)為bus_attr_drivers_probe,bus_attr_drivers_autoprobe.注冊(cè)bus_type中的屬性建立文件
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus,
bus_uevent_fail:
kset_unregister(
kfree(bus->p);out:
returnretval;}
這段代碼為出錯(cuò)處理
這段代碼中比較繁鎖的就是bus_type對(duì)應(yīng)目錄下的屬性文件建立,為了直觀的說(shuō)明,將屬性文件的建立統(tǒng)一放到一起分析
從上面的代碼中可以看,創(chuàng)立屬性文件對(duì)應(yīng)的屬性分別為:
bus_attr_ueventbus_attr_drivers_probe,bus_attr_drivers_autoprobe
分別定義如下:
staticBUS_ATTR(uevent,S_IWUSR,NULL,bus_uevent_store);
staticBUS_ATTR(drivers_probe,S_IWUSR,NULL,store_drivers_probe);
staticBUS_ATTR(drivers_autoprobe,S_IWUSR|S_IRUGO,
show_drivers_autoprobe,store_drivers_autoprobe);
BUS_ATTR定義如下:
#defineBUS_ATTR(_name,_mode,_show,_store)\\
structbus_attributebus_attr_##_name=__ATTR(_name,_mode,_show,_store)
#define__ATTR(_name,_mode,_show,_store){\\
.attr={.name=__stringify(_name),.mode=_mode},\\
.show=_show,\\
.store=_store,\\}
由此可見(jiàn).上面這三個(gè)屬性對(duì)應(yīng)的名稱為別為uevent,drivers_probe,drivers_autoprobe.也就是說(shuō),會(huì)在bus_types目錄下生成三個(gè)文件,分別為uevent,probe,autoprobe.
根據(jù)之前的分析,我們知道在sysfs文件系統(tǒng)中,對(duì)普通屬性文件的讀寫都會(huì)回溯到kobject->ktype->sysfs_ops中.在這里,注意到有:
priv->subsys.kobj.kset=bus_kset;
priv->subsys.kobj.ktype=
顯然,讀寫操作就回溯到了bus_ktype中.定義如下:
staticstructkobj_typebus_ktype={
.sysfs_ops=
staticstructsysfs_opsbus_sysfs_ops={
.show=bus_attr_show,
.store=bus_attr_store,};
Show和store函數(shù)對(duì)應(yīng)的代碼為:
staticssize_tbus_attr_show(structkobject*kobj,structattribute*attr,char*buf){
structbus_attribute*bus_attr=to_bus_attr(attr);
structbus_type_private*bus_priv=to_bus(kobj);
ssize_tret=0;
if(bus_attr->show)
ret=bus_attr->show(bus_priv->bus,buf);
returnret;}
staticssize_tbus_attr_store(structkobject*kobj,structattribute*attr,constchar*buf,size_tcount){
structbus_attribute*bus_attr=to_bus_attr(attr);
structbus_type_private*bus_priv=to_bus(kobj);
ssize_tret=0;
if(bus_attr->store)
ret=bus_attr->store(bus_priv->bus,buf,count);
returnret;}
從代碼可以看出.讀寫操作又會(huì)回溯到bus_attribute中的show和store中.在自定義結(jié)構(gòu)里嵌入structattribute,.然后再操作回溯到自定義結(jié)構(gòu)中,這是一種比較高明的架構(gòu)設(shè)計(jì)手法.
閑言少敘.我們對(duì)應(yīng)看一下上面三個(gè)文件對(duì)應(yīng)的最終操作:
Uevent對(duì)應(yīng)的讀寫操作為:NULL,bus_uevent_store.對(duì)于這個(gè)文件沒(méi)有讀操作,只有寫操作.用cat命令去查看這個(gè)文件的時(shí)候,可能會(huì)返回〞設(shè)備不存在〞的錯(cuò)誤.bus_uevent_store()代碼如下:
staticssize_tbus_uevent_store(structbus_type*bus,constchar*buf,size_tcount){
enumkobject_actionaction;
if(kobject_action_type(buf,count,
returncount;}
從這里可以看到,可以在用戶空間控制事件的發(fā)生,如echoadd>event就會(huì)產(chǎn)生一個(gè)add的事件,
Probe文件對(duì)應(yīng)的讀寫操作為:NULLstore_drivers_probe.
store_drivers_probe()這個(gè)函數(shù)的代碼涉及到structdevice.等分析完structdevice可以自行回過(guò)來(lái)看下這個(gè)函數(shù)的實(shí)現(xiàn).實(shí)際上,這個(gè)函數(shù)是將用戶輸和的設(shè)備名稱對(duì)應(yīng)的設(shè)備與驅(qū)動(dòng)匹配一次.
Autoprobe文件對(duì)應(yīng)的讀寫操作為show_drivers_autoprobe,store_drivers_autoprobe.對(duì)應(yīng)讀的代碼為:
staticssize_tshow_drivers_autoprobe(structbus_type*bus,char*buf){
returnsprintf(buf,\
}
它將總線對(duì)應(yīng)的drivers_autoprobe的值輸出到用戶空間,這個(gè)值為1時(shí),自動(dòng)將驅(qū)動(dòng)與設(shè)備進(jìn)行匹配.否則,反之.
寫操作的代碼如下:
staticssize_tstore_drivers_autoprobe(structbus_type*bus,
constchar*buf,size_tcount){
if(buf[0]=='0')
bus->p->drivers_autoprobe=0;
else
bus->p->drivers_autoprobe=1;
returncount;}
寫操作就會(huì)改變bus->p->drivers_autoprobe的值.
就這樣,通過(guò)sysfs就可以控制總線是否要進(jìn)行自動(dòng)匹配了.
從這里也可以看出.內(nèi)核開(kāi)發(fā)者的思維是何等的靈活.
我們從sysfs中找個(gè)例子來(lái)印證一下:
Cd/sys/bus/usb
用ls命令查看:
devicesdriversdrivers_autoprobedrivers_probeuevent
與上面分析的相吻合
設(shè)備的注冊(cè)接口為:device_register().
intdevice_register(structdevice*dev){
device_initialize(dev);
returndevice_add(dev);}
Device_initialize()中有幾個(gè)很重要的操作,如下:
voiddevice_initialize(structdevice*dev){
dev->kobj.kset=devices_kset;
kobject_init(
klist_init(
INIT_LIST_HEAD(
INIT_LIST_HEAD(
init_MUTEX(
spin_lock_init(
INIT_LIST_HEAD(
device_init_wakeup(dev,0);
set_dev_node(dev,-1);}
在這里,它為device的內(nèi)嵌kobject指定了ktype和kset.device_kset的值如下:
devices_kset=kset_create_and_add(\
即對(duì)應(yīng)sysfs中的/sys/devices
device_ktype中對(duì)屬性的讀寫操作同bus中的類似,被回溯到了structdevice_attribute中的show和store.
接著往下看device_add()的實(shí)現(xiàn).這個(gè)函數(shù)比較長(zhǎng),分段分析如下:
intdevice_add(structdevice*dev){
structdevice*parent=NULL;
structclass_interface*class_intf;
interror;
dev=get_device(dev);
if(!dev||!strlen(dev->bus_id)){
error=-EINVAL;
gotoDone;
}
pr_debug(\
parent=get_device(dev->parent);
setup_parent(dev,parent);
/*first,registerwithgenericlayer.*/
err
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 空調(diào)改造施工方案
- 休閑娛樂(lè)場(chǎng)所設(shè)計(jì)委托合同
- 保健品配送勞務(wù)合同范文
- 農(nóng)村客運(yùn)班車服務(wù)協(xié)議
- 展會(huì)物流服務(wù)合同樣本
- 企業(yè)辦公室翻新合同模板
- 個(gè)性化滑雪場(chǎng)裝修合同樣本
- 縣域快遞運(yùn)輸線路外包合同
- 2024-2030年廚房設(shè)備行業(yè)市場(chǎng)風(fēng)險(xiǎn)投資分析及運(yùn)作模式與投資融資策略研究報(bào)告
- 2024-2030年醫(yī)藥設(shè)備市場(chǎng)投資前景分析及供需格局研究預(yù)測(cè)報(bào)告
- 靜脈??谱o(hù)士匯報(bào)
- 企業(yè)采購(gòu)管理現(xiàn)狀、問(wèn)題及完善策略-以正泰電器集團(tuán)為例(論文)
- 嵌入式應(yīng)用開(kāi)發(fā)期末練習(xí)題
- 單髁置換完整版
- 義務(wù)教育英語(yǔ)課程標(biāo)準(zhǔn)2022版電子版
- 消防建筑設(shè)計(jì)防火規(guī)范
- 免疫系統(tǒng)的組成和功能 教學(xué)設(shè)計(jì)
- 加強(qiáng)供電企業(yè)青年員工培養(yǎng)模式優(yōu)化措施
- GB/T 2965-2007鈦及鈦合金棒材
- 中國(guó)腦出血診治指南(2023年)-1
- 藥品采購(gòu)供應(yīng)制度執(zhí)行情況檢查記錄
評(píng)論
0/150
提交評(píng)論