linux設(shè)備模型深探_第1頁(yè)
linux設(shè)備模型深探_第2頁(yè)
linux設(shè)備模型深探_第3頁(yè)
linux設(shè)備模型深探_第4頁(yè)
linux設(shè)備模型深探_第5頁(yè)
已閱讀5頁(yè),還剩43頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論