版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領
文檔簡介
本文格式為Word版,下載可任意編輯——linux設備模型深探一:前言
Linux設備模型是一個極其繁雜的結(jié)構(gòu)體系,在編寫驅(qū)動程序的時候,尋常不會用到這方面的東西,但是。理解這部份內(nèi)容,對于我們理解linux設備驅(qū)動的結(jié)構(gòu)是大有裨益的。我們不但可以在編寫程序程序的時候知其然,亦知其所以然。又可以學習到一種極其精致的架構(gòu)設計方法。由于之前已經(jīng)詳細分析了sysfs文件系統(tǒng)。所以本節(jié)的探討主要集中在設備模型的底層實現(xiàn)上。上層的接口,如pci.,usb,網(wǎng)絡設備都可以看成是底層的封裝。
二:kobject,kset和ktype
Kobject,kset,kypte這三個結(jié)構(gòu)是設備模型中的下層架構(gòu)。模型中的每一個元素都對應一個kobject.kset和ktype可以看成是kobject在層次結(jié)構(gòu)與屬性結(jié)構(gòu)方面的擴展。將三者之間的關(guān)系用圖的方示描述如下:
如上圖所示:我們知道。在sysfs中每一個目錄都對應一個kobject.這些kobject都有自己的parent。在沒有指定parent的狀況下,都會指向它所屬的kset->object。其次,kset也內(nèi)嵌了kobject.這個kobject又可以指它上一級的parent。就這樣。構(gòu)成了一個空間上面的層次關(guān)系。
其實,每個對象都有屬性。例如,電源管理,執(zhí)插撥事性管理等等。由于大部份的同類設備都有一致的屬性,因此將這個屬性隔離開來,存放在ktype中。這樣就可以靈活的管理了.記得在分析sysfs的時候。對于sysfs中的普通文件讀寫操作都是由kobject->ktype->sysfs_ops來完成的.
經(jīng)過上面的分析,我們大約了解了kobject.kset與ktype的大約架設與相互之間的關(guān)系。下面我們從linux源代碼中的分析來詳細研究他們的操作。
三:kobject,kset和ktype的操作
為了說明kobject的操作,先寫一個測試模塊,代碼如下:
#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);
加載模塊之后,會發(fā)現(xiàn),在/sys下多了一個eric_test目錄。該目錄下有一個叫eric_xiao的文件。如下所示:
[root@localhosteric_test]#ls
eric_xiao
用cat觀測此文件:
[root@localhosteric_test]#cateric_xiao
eric_xiao
再用echo往里面寫點東西;
[root@localhosteric_test]#echohello>eric_xiao
Dmesg的輸出如下:
haveshow.
attrname:eric_xiao.
havestore
write:hello
如上所示。我們看到了kobject的大約建立過程.我們來看一下kobject_init_and_add()的實現(xiàn)。在這個函數(shù)里,包含了對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設置名稱,在sysfs中建立相關(guān)信息
retval=kobject_add_varg(kobj,parent,fmt,args);
va_end(args);
returnretval;}
上面的流程主要分為兩部份。一部份是kobject的初始化。在這一部份,它將kobject與給定的ktype關(guān)聯(lián)起來。初始化kobject中的各項結(jié)構(gòu)。另一部份是kobject的名稱設置??臻g層次關(guān)系的設置,具體表現(xiàn)在sysfs文件系統(tǒng)中.
對于第一部份,代碼比較簡單,這里不再贅述。跟蹤其次部份,也就是kobject_add_varg()的實現(xiàn).
staticintkobject_add_varg(structkobject*kobj,structkobject*parent,
constchar*fmt,va_listvargs){
va_listaq;
intretval;
va_copy(aq,vargs);
//設置kobject的名字。即kobject的name成員
retval=kobject_set_name_vargs(kobj,fmt,aq);
va_end(aq);
if(retval){
printk(KERN_ERR\
returnretval;
}
//設置kobject的parent。在上面的例子中,我們沒有給它指定父結(jié)點
kobj->parent=parent;
//在sysfs中添加kobjcet信息
returnkobject_add_internal(kobj);}
設置好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é)點
parent=kobject_get(kobj->parent);
//假使kobject的父結(jié)點沒有指定,就將kset->kobject做為它的父結(jié)點
/*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)的引用計數(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;}
這段代碼比較簡單,它主要完成kobject父結(jié)點的判斷和選定,然后再調(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;}
我們在上面的例如中看到的/sys下的eric_test目錄,以及該目錄下面的eric_xiao的這個文件就是這里被創(chuàng)立的。我們先看一下kobject所表示的目錄創(chuàng)立過程。這是在sysfs_create_dir()中完成的。代碼如下:
intsysfs_create_dir(structkobject*kobj){
structsysfs_dirent*parent_sd,*sd;
interror=0;
BUG_ON(!kobj);
/*假使kobject的parnet存在。就在目錄點的目錄下創(chuàng)立這個目錄。假使沒有父結(jié)點不存在,就在/sys下面創(chuàng)立結(jié)點。在上面的流程中,我們可能并沒有為其指定父結(jié)點,也沒有為其指定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)系之前分析過的sysfs文件系統(tǒng)的研究了。假使不太明白的,可以在找到那篇文章細心的研讀一下。create_dir()就是在sysfs中創(chuàng)立目錄的接口,在之前已經(jīng)詳細分析過了。這里不再陳述。
接著看為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;}
這段代碼比較簡單。它遍歷ktype中的屬性。然后為其建立文件。請注意:文件的操作最終都會回溯到ktype->sysfs_ops的show和store這兩個函數(shù)中.
Kobject的創(chuàng)立已經(jīng)分析完了,接著分析怎么將一個kobject注銷掉。注意過程是在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ù)會將在sysfs中的kobject對應的目錄刪除。請注意,屬性文件是建立在這個目錄下面的。只需要將這個目錄刪除。屬性文件也隨之刪除。
是后,減少相關(guān)的引用計數(shù),假使kobject的引用計數(shù)為零。則將其所占空間釋放.
Kset的操作與kobject類似,由于kset中內(nèi)嵌了一個kobject結(jié)構(gòu),所以,大部份操作都是集中在kset->kobject上.具體分析一下kset_create_and_add()這個接口,類似上面分析的kobject接口,這個接口也包括了kset的大部分操作.代碼如下:
structkset*kset_create_and_add(constchar*name,structkset_uevent_ops*uevent_ops,structkobject*parent_kobj){
structkset*kset;
interror;
//創(chuàng)立一個kset
kset=kset_create(name,uevent_ops,parent_kobj);
if(!kset)
returnNULL;
//注冊kset
error=kset_register(kset);
if(error){
//假使注冊失敗,釋放kset
kfree(kset);
returnNULL;
}
returnkset;}
Kset_create()用來創(chuàng)立一個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時.為其內(nèi)嵌的kobject指定其ktype結(jié)構(gòu)為kset_ktype.這個結(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成員對應的函數(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;}
從上面的代碼看以看出.會將structattribute結(jié)構(gòu)轉(zhuǎn)換為structkobj_attribte結(jié)構(gòu).也就是說structkobj_attribte內(nèi)嵌了一個structattribute.實際上,這是和宏__ATTR協(xié)同在一起使用的.經(jīng)常用于group中.在這里并不計劃研究group.原理都是一樣的.這里列出來只是做個說明而已.
創(chuàng)立好了kset之后,會調(diào)用kset_register().這個函數(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()里會初始化kset中的其它字段.然后調(diào)用kobject_add_internal()為其內(nèi)嵌的
kobject結(jié)構(gòu)建立空間層次結(jié)構(gòu).之后由于添加了kset.會產(chǎn)生一個事件.這個事件是通過用戶空間的hotplug程序處理的.這就是kset明顯不同于kobject的地方.詳細研究一下這個函數(shù).這對于我們研究hotplug的深層機理是很有幫助的.它的代碼如下;
intkobject_uevent(structkobject*kobj,enumkobject_actionaction){
returnkobject_uevent_env(kobj,action,NULL);}
之后,會調(diào)用kobject_uevent_env().這個函數(shù)中的三個參數(shù)含義分別為:引起事件的kobject.事件類型(add,remove,change,move,online,offline等).第三個參數(shù)是要添加的環(huán)境變量.
代碼篇幅較長,我們效仿情景分析上面的做法.分段分析如下:
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;
}
由于對事件的處理函數(shù)包含在kobject->kset->uevent_ops中.要處理事件,就必需要找到上層的一個不為空的kset.上面的代碼就是順著kobject->parent找不到一個不為空的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()匹配.看這個事件是否被過濾.假使沒有被過濾掉.就會調(diào)用kset->uevent_ops->name()得到子系統(tǒng)的名稱,假使不存在kset->uevent_ops->name().就會以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;
}
}
接下來,就應當設置為調(diào)用hotplug設置環(huán)境變量了.首先,分派一個structkobj_uevent_env結(jié)構(gòu)用來存放環(huán)境變量的值.然后調(diào)用kobject_get_path()用來獲得引起事件的kobject在sysfs中的路徑.再調(diào)用add_uevent_var()將動作代表的字串,kobject路徑,子系統(tǒng)名稱填充到structkobj_uevent_env中,假使有指定環(huán)境變量,也將其添加進去.kobject_get_path()和add_uevent_var()都比較簡單.這里不再詳細分析了.請自行查看源代碼
/*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;
在這里還會調(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了.添加最終兩個環(huán)境變量.HOME和PATH.然后調(diào)用hotplug.以子系統(tǒng)名稱為參數(shù).
現(xiàn)在我們終究知道hotplug處理程序中的參數(shù)和環(huán)境變量是怎么來的了.^_^
使用完了kset.再調(diào)用kset_unregister()將其注銷.這個函數(shù)很簡單,請自行查閱代碼.
為了印證一下上面的分析,寫一個測試模塊。如下:
#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);
在這里,定義并注冊了二個kset.其次個kset的kobj->kset域指向第一個kset.這樣,當其次個kset注冊或者卸載的時候就會調(diào)用第一個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)境變量全部打印出來。
下面是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é)果跟我們的分析是吻合的.
在這里,值得我們注意的是。注冊一個kobject不會產(chǎn)生事件,只有注冊kset才會.
四:bus,device和device_driver
上面分析了kobject.kset,ktype.這三個結(jié)構(gòu)聯(lián)合起來一起構(gòu)成了整個設備模型的基石.而bus.device.device_driver.則是基于kobject.kset.ktype之上的架構(gòu).在這里,總線,設備,驅(qū)動被有序的組和在一起.
Bus.device.device_driver三者之間的關(guān)系如下圖所示:
如上圖所示.structbus_type的p->drivers_kset指向注冊在上面的驅(qū)動程序.它的p->device_kset上掛著注冊在上面的設備.每次有一個新的設備注冊到上面,都會去匹配右邊的驅(qū)動,看是否能匹配上.假使匹配成功,則將設備結(jié)構(gòu)的is_registerd域置為0.然后將設備添加到驅(qū)動的p->klist_devices域.同理,每注冊一個驅(qū)動,都會去匹配左邊的設備,.假使匹配成功,將則設備加到驅(qū)動的p->klist_devices域.再將設備的is_registerd置為0/
這就是linux設備模型用來管理設備和驅(qū)動的基本架構(gòu).我們來跟蹤一下代碼來看下詳細的操作.
注冊一個總線的接口為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)起來.由于structbus_type也要在sysfs文件中表示一個節(jié)點,因此,它也內(nèi)嵌也一個kset的結(jié)構(gòu).這就是priv->subsys.
首先,它為這個kset的名稱賦值為bus的名稱,然后將priv->subsys.kobj.kset指向bus_kset.priv->subsys.kobj.ktype指向bus_ktype;然后調(diào)用kset_reqister()將priv->subsys注冊.這里涉及到的接口都在之前分析過.注冊過后,應當會在bus_kset所表示的目錄下創(chuàng)立一個總線名稱的目錄.并且用戶空間的hotplug應當會檢測到一個add事件.我們來看一下bus_kset終究指向的是什么:
bus_kset=kset_create_and_add(\
此后可以看出.這個bus_kset在sysfs中的結(jié)點就是/sys/bus.在這里注冊的structbus_types就
會在/sys/bus/下面出現(xiàn).
retval=bus_create_file(bus,
if(retval)
gotobus_uevent_fail;
bus_create_file()就是在priv->subsys.kobj的這個kobject上建立一個普通屬性的文件.這個文件的屬性對應在bus_attr_uevent.讀寫操作對應在priv->subsys.ktype中.我們到后面才統(tǒng)一分析bus注冊時候的文件創(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(
這段代碼會在bus所在的目錄下建立兩個目錄,分別為devices和drivers.并初始化掛載設備和驅(qū)動的鏈表
retval=add_probe_files(bus);
if(retval)
gotobus_probe_files_fail;
retval=bus_add_attrs(bus);
if(retval)
gotobus_attrs_fail;
pr_debug(\
return0;
在這里,會為bus_attr_drivers_probe,bus_attr_drivers_autoprobe.注冊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;}
這段代碼為出錯處理
這段代碼中比較繁鎖的就是bus_type對應目錄下的屬性文件建立,為了直觀的說明,將屬性文件的建立統(tǒng)一放到一起分析
從上面的代碼中可以看,創(chuà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,\\}
由此可見.上面這三個屬性對應的名稱為別為uevent,drivers_probe,drivers_autoprobe.也就是說,會在bus_types目錄下生成三個文件,分別為uevent,probe,autoprobe.
根據(jù)之前的分析,我們知道在sysfs文件系統(tǒng)中,對普通屬性文件的讀寫都會回溯到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ù)對應的代碼為:
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;}
從代碼可以看出.讀寫操作又會回溯到bus_attribute中的show和store中.在自定義結(jié)構(gòu)里嵌入structattribute,.然后再操作回溯到自定義結(jié)構(gòu)中,這是一種比較高明的架構(gòu)設計手法.
閑言少敘.我們對應看一下上面三個文件對應的最終操作:
Uevent對應的讀寫操作為:NULL,bus_uevent_store.對于這個文件沒有讀操作,只有寫操作.用cat命令去查看這個文件的時候,可能會返回〞設備不存在〞的錯誤.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就會產(chǎn)生一個add的事件,
Probe文件對應的讀寫操作為:NULLstore_drivers_probe.
store_drivers_probe()這個函數(shù)的代碼涉及到structdevice.等分析完structdevice可以自行回過來看下這個函數(shù)的實現(xiàn).實際上,這個函數(shù)是將用戶輸和的設備名稱對應的設備與驅(qū)動匹配一次.
Autoprobe文件對應的讀寫操作為show_drivers_autoprobe,store_drivers_autoprobe.對應讀的代碼為:
staticssize_tshow_drivers_autoprobe(structbus_type*bus,char*buf){
returnsprintf(buf,\
}
它將總線對應的drivers_autoprobe的值輸出到用戶空間,這個值為1時,自動將驅(qū)動與設備進行匹配.否則,反之.
寫操作的代碼如下:
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;}
寫操作就會改變bus->p->drivers_autoprobe的值.
就這樣,通過sysfs就可以控制總線是否要進行自動匹配了.
從這里也可以看出.內(nèi)核開發(fā)者的思維是何等的靈活.
我們從sysfs中找個例子來印證一下:
Cd/sys/bus/usb
用ls命令查看:
devicesdriversdrivers_autoprobedrivers_probeuevent
與上面分析的相吻合
設備的注冊接口為:device_register().
intdevice_register(structdevice*dev){
device_initialize(dev);
returndevice_add(dev);}
Device_initialize()中有幾個很重要的操作,如下:
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(\
即對應sysfs中的/sys/devices
device_ktype中對屬性的讀寫操作同bus中的類似,被回溯到了structdevice_attribute中的show和store.
接著往下看device_add()的實現(xiàn).這個函數(shù)比較長,分段分析如下:
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. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年河源道路運輸從業(yè)資格考試系統(tǒng)
- 2024-2025學年新教材高中語文第六單元課時優(yōu)案5拿來主義習題含解析新人教版必修上冊
- 光學實驗室建設方案
- 華師大版數(shù)學八年級下冊《平面直角坐標系》聽評課記錄
- 高中老師工作總結(jié)
- 個人培訓研修計劃
- 實驗教學聽評課記錄
- 餐飲合伙人合同范本
- 應急照明施工合同范本
- 華中農(nóng)業(yè)大學《礦井熱害防治》2023-2024學年第二學期期末試卷
- 三高疾病之中醫(yī)辨證施治
- 醫(yī)療器械銷售目標分解
- 射線衍射原理
- 中華全國總工會辦公廳印發(fā)《加快工會數(shù)字化建設工作方案》
- 成人住院患者靜脈血栓栓塞癥預防護理
- 《3-6歲兒童學習與發(fā)展指南》健康領域內(nèi)容目標與指導
- GB/T 10739-2023紙、紙板和紙漿試樣處理和試驗的標準大氣條件
- 《云臺山地質(zhì)公園》課件
- 環(huán)保行業(yè)研究報告
- 彈藥安全管理知識講座
- 消化內(nèi)科肝硬化“一病一品”
評論
0/150
提交評論