您的位置:首页 > 运维架构 > Linux

Linux设备模型分析之kset(基于3.10.1内核)

2013-10-29 21:46 531 查看
作者:刘昊昱博客:http://blog.csdn.net/liuhaoyutz内核版本:3.10.1

一、kset结构定义

kset结构体定义在include/linux/kobject.h文件中,其内容如下:

142/**
143*structkset-asetofkobjectsofaspecifictype,belongingtoaspecificsubsystem.
144*
145*Aksetdefinesagroupofkobjects.Theycanbeindividually
146*different"types"butoverallthesekobjectsallwanttobegrouped
147*togetherandoperatedoninthesamemanner.ksetsareusedto
148*definetheattributecallbacksandothercommoneventsthathappento
149*akobject.
150*
151*@list:thelistofallkobjectsforthiskset
152*@list_lock:alockforiteratingoverthekobjects
153*@kobj:theembeddedkobjectforthiskset(recursion,isn'titfun...)
154*@uevent_ops:thesetofueventoperationsforthiskset.Theseare
155*calledwheneverakobjecthassomethinghappentoitsothatthekset
156*canaddnewenvironmentvariables,orfilterouttheueventsifso
157*desired.
158*/
159structkset{
160structlist_headlist;
161spinlock_tlist_lock;
162structkobjectkobj;
163conststructkset_uevent_ops*uevent_ops;
164};

从注释可以看出,kset是一组kobject的集合,这些kobject可以具有不同的“types”,下面来看kset的成员变量:

list用于将该kset下的所有kobject链接成一个链表。

list_lock是一个自旋锁,在遍历该kset下的kobject时用来加锁。

kobj是代表该kset的一个kobject。

uevent_ops是一组函数指针,当kset中的某个kobject状态发生变化需要通知用户空间时,就通过这些函数来完成。uevent_ops是structkset_uevent_ops类型,该结构体定义在include/linux/kobject.h文件中,其定义如下:

123structkset_uevent_ops{
124int(*constfilter)(structkset*kset,structkobject*kobj);
125constchar*(*constname)(structkset*kset,structkobject*kobj);
126int(*constuevent)(structkset*kset,structkobject*kobj,
127structkobj_uevent_env*env);
128};

关于kset_uevent_ops结构体中的成员函数的作用,我们后面再分析。


二、kset的创建和注册

要创建并注册一个kset,使用的是kset_create_and_add函数,该函数定义在lib/kobject.c文件中,其内容如下:

827/**
828*kset_create_and_add-createastructksetdynamicallyandaddittosysfs
829*
830*@name:thenameforthekset
831*@uevent_ops:astructkset_uevent_opsforthekset
832*@parent_kobj:theparentkobjectofthiskset,ifany.
833*
834*Thisfunctioncreatesaksetstructuredynamicallyandregistersit
835*withsysfs.Whenyouarefinishedwiththisstructure,call
836*kset_unregister()andthestructurewillbedynamicallyfreedwhenit
837*isnolongerbeingused.
838*
839*Iftheksetwasnotabletobecreated,NULLwillbereturned.
840*/
841structkset*kset_create_and_add(constchar*name,
842conststructkset_uevent_ops*uevent_ops,
843structkobject*parent_kobj)
844{
845structkset*kset;
846interror;
847
848kset=kset_create(name,uevent_ops,parent_kobj);
849if(!kset)
850returnNULL;
851error=kset_register(kset);
852if(error){
853kfree(kset);
854returnNULL;
855}
856returnkset;
857}

828行,从注释可以看出,kset_create_and_add函数的作用是动态创建一个kset结构并把它注册到sysfs文件系统中。注意该函数的三个参数:

name是kset的名字,它会被赋值给kset.kobj.name。

uevent_ops是structkset_uevent_ops变量,它会被赋值给kset.uevent_ops。

parent_kobj是该kset的父kobject,它会被赋值给kset.kobj.parent。

848行,调用kset_create函数动态创建kset结构并对其进行初始化,该函数定义在lib/kobject.c文件中,其内容如下:

783/**
784*kset_create-createastructksetdynamically
785*
786*@name:thenameforthekset
787*@uevent_ops:astructkset_uevent_opsforthekset
788*@parent_kobj:theparentkobjectofthiskset,ifany.
789*
790*Thisfunctioncreatesaksetstructuredynamically.Thisstructurecan
791*thenberegisteredwiththesystemandshowupinsysfswithacallto
792*kset_register().Whenyouarefinishedwiththisstructure,if
793*kset_register()hasbeencalled,callkset_unregister()andthe
794*structurewillbedynamicallyfreedwhenitisnolongerbeingused.
795*
796*Iftheksetwasnotabletobecreated,NULLwillbereturned.
797*/
798staticstructkset*kset_create(constchar*name,
799conststructkset_uevent_ops*uevent_ops,
800structkobject*parent_kobj)
801{
802structkset*kset;
803intretval;
804
805kset=kzalloc(sizeof(*kset),GFP_KERNEL);
806if(!kset)
807returnNULL;
808retval=kobject_set_name(&kset->kobj,name);
809if(retval){
810kfree(kset);
811returnNULL;
812}
813kset->uevent_ops=uevent_ops;
814kset->kobj.parent=parent_kobj;
815
816/*
817*Thekobjectofthisksetwillhaveatypeofkset_ktypeandbelongto
818*noksetitself.Thatwaywecanproperlyfreeitwhenitis
819*finishedbeingused.
820*/
821kset->kobj.ktype=&kset_ktype;
822kset->kobj.kset=NULL;
823
824returnkset;
825}

805行,为kset结构分配内存空间。

808行,将name参数赋值给kset.kobj.name。它对应kset在sysfs文件系统中的目录名。

813行,将uevent_ops赋值给kset->uevent_ops。

814行,将parent_kobj赋值给kset->kobj.parent。

816-822行,由注释可以知道,kset.kobj.ktype被赋于一个kset_ktype类型,并且kset.kobj.kset为NULL,即该kset不属于任何其它kset。这样可以保证在不再继续使用该kset时可以正确的释放它。这里我们要来看一下kset_ktype的定义,它定义在lib/kobject.c文件中,其内容如下:

778staticstructkobj_typekset_ktype={
779.sysfs_ops=&kobj_sysfs_ops,
780.release=kset_release,
781};

kobj_sysfs_ops定义在lib/kobject.c文件中,其内容如下:

708conststructsysfs_opskobj_sysfs_ops={
709.show=kobj_attr_show,
710.store=kobj_attr_store,
711};

结合上篇文章中对kobject的分析,我们可以得出如下结论:

如果用户空间程序要对kset对应的sysfs文件系统下的属性文件进行读操作时,kobj_attr_show函数会被调用。

如果用户空间程序要对kset对应的sysfs文件系统下的属性文件进行写操作时,

kobj_attr_store函数会被调用。

下面我们来看kobj_attr_show函数,它定义在lib/kobject.c文件中:

683/*defaultkobjectattributeoperations*/
684staticssize_tkobj_attr_show(structkobject*kobj,structattribute*attr,
685char*buf)
686{
687structkobj_attribute*kattr;
688ssize_tret=-EIO;
689
690kattr=container_of(attr,structkobj_attribute,attr);
691if(kattr->show)
692ret=kattr->show(kobj,kattr,buf);
693returnret;
694}

注意683行的注释,这是默认的kobjectattribute操作函数。在这函数中,通过container_of取得包含attr变量的structkobj_attribute变量kattr,然后调用kattr->show()函数。

kobj_attr_store函数与kobj_attr_show函数类似,同样定义在lib/kobject.c文件中:

696staticssize_tkobj_attr_store(structkobject*kobj,structattribute*attr,
697constchar*buf,size_tcount)
698{
699structkobj_attribute*kattr;
700ssize_tret=-EIO;
701
702kattr=container_of(attr,structkobj_attribute,attr);
703if(kattr->store)
704ret=kattr->store(kobj,kattr,buf,count);
705returnret;
706}

在该函数中,通过container_of取得包含attr变量的structkobj_attribute变量kattr,然后调用kattr->store()函数。

这样,如果用户空间程序要对kset对应的sysfs文件系统下的属性文件进行读写操作时,就会转而调用包含相应attribute的kobj_attribute结构体的show/store函数。实际上这种用法是和宏__ATTR结合在一起使用的,后面我们会再分析。

到此,kobject_create函数我们就分析完了,回到kset_create_and_add函数,

851行,调用kset_register(kset)函数注册kset,该函数定义在lib/kobject.c文件中,其内容如下:

713/**
714*kset_register-initializeandaddakset.
715*@k:kset.
716*/
717intkset_register(structkset*k)
718{
719interr;
720
721if(!k)
722return-EINVAL;
723
724kset_init(k);
725err=kobject_add_internal(&k->kobj);
726if(err)
727returnerr;
728kobject_uevent(&k->kobj,KOBJ_ADD);
729return0;
730}

724行,首先对kset进行初始化。kset的初始化是通过调用kset_init函数完成的,该函数定义在lib/kobject.c文件中,其内容如下:

672/**
673*kset_init-initializeaksetforuse
674*@k:kset
675*/
676voidkset_init(structkset*k)
677{
678kobject_init_internal(&k->kobj);
679INIT_LIST_HEAD(&k->list);
680spin_lock_init(&k->list_lock);
681}

可见,只是简单初始化kset.kobj,kset.list,和kset.list_lock。

725行,将kset.kobj加入到kobject层次结构和sysfs文件系统中。

728行,调用kobject_uevent(&k->kobj,KOBJ_ADD),通知用户空间添加了一个kobject,即kset.kobj。kobject_uevent函数定义在lib/kobject_uevent.c文件中,其内容如下:

322/**
323*kobject_uevent-notifyuserspacebysendinganuevent
324*
325*@action:actionthatishappening
326*@kobj:structkobjectthattheactionishappeningto
327*
328*Returns0ifkobject_uevent()iscompletedwithsuccessorthe
329*correspondingerrorwhenitfails.
330*/
331intkobject_uevent(structkobject*kobj,enumkobject_actionaction)
332{
333returnkobject_uevent_env(kobj,action,NULL);
334}

从注释可以看出,kobject_uevent函数的作用是通过发送一个uevent通知用户空间内核中发生了某些事情。至于发生了什么事情,由第二个参数action指定,action是enumkobject_action类型变量,定义在include/linux/kobject.h文件中,其内容如下:

40/*
41*Theactionsheremustmatchtheindextothestringarray
42*inlib/kobject_uevent.c
43*
44*Donotaddnewactionsherewithoutcheckingwiththedriver-core
45*maintainers.Actionstringsarenotmeanttoexpresssubsystem
46*ordevicespecificproperties.Inmostcasesyouwanttosenda
47*kobject_uevent_env(kobj,KOBJ_CHANGE,env)withadditionalevent
48*specificvariablesaddedtotheeventenvironment.
49*/
50enumkobject_action{
51KOBJ_ADD,
52KOBJ_REMOVE,
53KOBJ_CHANGE,
54KOBJ_MOVE,
55KOBJ_ONLINE,
56KOBJ_OFFLINE,
57KOBJ_MAX
58};

可见,一共有这7种事件可以通知用户空间。

回到kobject_uevent函数,333行,调用kobject_uevent_env函数来发送uevent,该函数定义在lib/kobject_uevent.c文件中,其内容如下:

121/**
122*kobject_uevent_env-sendanueventwithenvironmentaldata
123*
124*@action:actionthatishappening
125*@kobj:structkobjectthattheactionishappeningto
126*@envp_ext:pointertoenvironmentaldata
127*
128*Returns0ifkobject_uevent_env()iscompletedwithsuccessorthe
129*correspondingerrorwhenitfails.
130*/
131intkobject_uevent_env(structkobject*kobj,enumkobject_actionaction,
132char*envp_ext[])
133{
134structkobj_uevent_env*env;
135constchar*action_string=kobject_actions[action];
136constchar*devpath=NULL;
137constchar*subsystem;
138structkobject*top_kobj;
139structkset*kset;
140conststructkset_uevent_ops*uevent_ops;
141inti=0;
142intretval=0;
143#ifdefCONFIG_NET
144structuevent_sock*ue_sk;
145#endif
146
147pr_debug("kobject:'%s'(%p):%s\n",
148kobject_name(kobj),kobj,__func__);
149
150/*searchtheksetwebelongto*/
151top_kobj=kobj;
152while(!top_kobj->kset&&top_kobj->parent)
153top_kobj=top_kobj->parent;
154
155if(!top_kobj->kset){
156pr_debug("kobject:'%s'(%p):%s:attemptedtosenduevent"
157"withoutkset!\n",kobject_name(kobj),kobj,
158__func__);
159return-EINVAL;
160}
161
162kset=top_kobj->kset;
163uevent_ops=kset->uevent_ops;
164
165/*skiptheevent,ifuevent_suppressisset*/
166if(kobj->uevent_suppress){
167pr_debug("kobject:'%s'(%p):%s:uevent_suppress"
168"causedtheeventtodrop!\n",
169kobject_name(kobj),kobj,__func__);
170return0;
171}
172/*skiptheevent,ifthefilterreturnszero.*/
173if(uevent_ops&&uevent_ops->filter)
174if(!uevent_ops->filter(kset,kobj)){
175pr_debug("kobject:'%s'(%p):%s:filterfunction"
176"causedtheeventtodrop!\n",
177kobject_name(kobj),kobj,__func__);
178return0;
179}
180
181/*originatingsubsystem*/
182if(uevent_ops&&uevent_ops->name)
183subsystem=uevent_ops->name(kset,kobj);
184else
185subsystem=kobject_name(&kset->kobj);
186if(!subsystem){
187pr_debug("kobject:'%s'(%p):%s:unsetsubsystemcausedthe"
188"eventtodrop!\n",kobject_name(kobj),kobj,
189__func__);
190return0;
191}
192
193/*environmentbuffer*/
194env=kzalloc(sizeof(structkobj_uevent_env),GFP_KERNEL);
195if(!env)
196return-ENOMEM;
197
198/*completeobjectpath*/
199devpath=kobject_get_path(kobj,GFP_KERNEL);
200if(!devpath){
201retval=-ENOENT;
202gotoexit;
203}
204
205/*defaultkeys*/
206retval=add_uevent_var(env,"ACTION=%s",action_string);
207if(retval)
208gotoexit;
209retval=add_uevent_var(env,"DEVPATH=%s",devpath);
210if(retval)
211gotoexit;
212retval=add_uevent_var(env,"SUBSYSTEM=%s",subsystem);
213if(retval)
214gotoexit;
215
216/*keyspassedinfromthecaller*/
217if(envp_ext){
218for(i=0;envp_ext[i];i++){
219retval=add_uevent_var(env,"%s",envp_ext[i]);
220if(retval)
221gotoexit;
222}
223}
224
225/*lettheksetspecificfunctionadditsstuff*/
226if(uevent_ops&&uevent_ops->uevent){
227retval=uevent_ops->uevent(kset,kobj,env);
228if(retval){
229pr_debug("kobject:'%s'(%p):%s:uevent()returned"
230"%d\n",kobject_name(kobj),kobj,
231__func__,retval);
232gotoexit;
233}
234}
235
236/*
237*Mark"add"and"remove"eventsintheobjecttoensureproper
238*eventstouserspaceduringautomaticcleanup.Iftheobjectdid
239*sendan"add"event,"remove"willautomaticallygeneratedby
240*thecore,ifnotalreadydonebythecaller.
241*/
242if(action==KOBJ_ADD)
243kobj->state_add_uevent_sent=1;
244elseif(action==KOBJ_REMOVE)
245kobj->state_remove_uevent_sent=1;
246
247mutex_lock(&uevent_sock_mutex);
248/*wewillsendanevent,sorequestanewsequencenumber*/
249retval=add_uevent_var(env,"SEQNUM=%llu",(unsignedlonglong)++uevent_seqnum);
250if(retval){
251mutex_unlock(&uevent_sock_mutex);
252gotoexit;
253}
254
255#ifdefined(CONFIG_NET)
256/*sendnetlinkmessage*/
257list_for_each_entry(ue_sk,&uevent_sock_list,list){
258structsock*uevent_sock=ue_sk->sk;
259structsk_buff*skb;
260size_tlen;
261
262if(!netlink_has_listeners(uevent_sock,1))
263continue;
264
265/*allocatemessagewiththemaximumpossiblesize*/
266len=strlen(action_string)+strlen(devpath)+2;
267skb=alloc_skb(len+env->buflen,GFP_KERNEL);
268if(skb){
269char*scratch;
270
271/*addheader*/
272scratch=skb_put(skb,len);
273sprintf(scratch,"%s@%s",action_string,devpath);
274
275/*copykeystoourcontinuouseventpayloadbuffer*/
276for(i=0;i<env->envp_idx;i++){
277len=strlen(env->envp[i])+1;
278scratch=skb_put(skb,len);
279strcpy(scratch,env->envp[i]);
280}
281
282NETLINK_CB(skb).dst_group=1;
283retval=netlink_broadcast_filtered(uevent_sock,skb,
2840,1,GFP_KERNEL,
285kobj_bcast_filter,
286kobj);
287/*ENOBUFSshouldbehandledinuserspace*/
288if(retval==-ENOBUFS||retval==-ESRCH)
289retval=0;
290}else
291retval=-ENOMEM;
292}
293#endif
294mutex_unlock(&uevent_sock_mutex);
295
296/*calluevent_helper,usuallyonlyenabledduringearlyboot*/
297if(uevent_helper[0]&&!kobj_usermode_filter(kobj)){
298char*argv[3];
299
300argv[0]=uevent_helper;
301argv[1]=(char*)subsystem;
302argv[2]=NULL;
303retval=add_uevent_var(env,"HOME=/");
304if(retval)
305gotoexit;
306retval=add_uevent_var(env,
307"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
308if(retval)
309gotoexit;
310
311retval=call_usermodehelper(argv[0],argv,
312env->envp,UMH_WAIT_EXEC);
313}
314
315exit:
316kfree(devpath);
317kfree(env);
318returnretval;
319}

122行,从注释可以看出,kobject_uevent_env函数的作用是发送带有环境变量数据的uevent。

150-160行,查找kobject所属的kset,如果这个kobject没有所属的kset,则看这个kobject.parent有没有所属的kset,如果还没有,继续沿着kobject层次结构树向上查找,直到找到一个具有所属kset的祖先kobject,如果确实没有找到,则出错退出。所以当前kobject的层次结构树中,必须有一个具有所属的kset。因为对事件的处理函数包含在kobject.kset.uevent_ops中,要处理事件,就必须找到上层一个不为空的kset。

值得注意的是,在创建kset的过程中,kset_create_and_add->kset_create,在kset_create函数中,将kset.kobj.kset设置为NULL,所以kset.kobj本身没有所属的kset,但是同样在kset_create函数中,kset.kobj.parent设置为parent_kobj,所以kset.kobj必然通过其上层祖先查找kset。

162行,取得相应的kset。

163行,将kset.uevent_ops赋值给uevent_ops变量。

165-171行,如果kobj->uevent_suppress被设置为1,则不发送uevent,退出。

172-179行,如果uevent_ops->filter(kset,kobj)返回值为0,说明kobj希望发送的uevent被顶层kset过滤掉了,不再发送。

181-191行,通过uevent_ops->name函数取得子系统名,如果uevent_ops->name为NULL,则使用kset.kobj.name做为子系统名。事实上,一个kset就是一个所谓的“subsystem”。

194行,分配structkobj_uevent_env变量空间给env,该结构体用来保存环境变量,它定义在include/linux/kobject.h文件中,其内容如下:

116structkobj_uevent_env{

117char*envp[UEVENT_NUM_ENVP];

118intenvp_idx;

119charbuf[UEVENT_BUFFER_SIZE];

120intbuflen;

121};

199行,调用kobject_get_path取得kobject的绝对路径。

205-214行,调用add_uevent_var函数将ACTION、DEVPATH、SUBSYSTEM三个默认环境变量添加到env中。add_uevent_var函数定义在lib/kobject_uevent.c文件中,其作用是“addkeyvaluestringtotheenvironmentbuffer”。

217-223行,如果调用kobject_uevent_env函数时,通过第三个参数envp_ext传递进来了其它相关环境变量,也通过add_uevent_var函数添加到env中。

225-234行,如果uevent_ops->uevent不为空,则调用uevent_ops->uevent,kset可以通过该函数完成自己特定的功能。

236-246行,如果action是KOBJ_ADD,则设置kobj->state_add_uevent_sent为1。如果action是KOBJ_REMOVE,则设置kobj->state_remove_uevent_sent为1。其作用注释中说的很清楚“Mark"add"and"remove"eventsintheobjecttoensurepropereventstouserspaceduringautomaticcleanup.Iftheobjectdidsendan"add"event,"remove"willautomaticallygeneratedbythecore,ifnotalreadydonebythecaller.”。

249行,将SEQNUM环境变量添加到env中。

kobject_uevent_env函数剩下的部分,用来和用户空间进程进行交互(或者在内核空间启动执行一个用户空间程序)。在Linux中,有两种方式完成这种交互,一个是代码中由CONFIG_NET宏包含的部分,即255-293行,这部分代码通过udev的方式向用户空间广播当前kset对象中的uevent事件。另外一种方式是在内核空间启动一个用户空间进程/sbin/hotplug,通过给该进程传递内核设定的环境变量的方式来通知用户空间kset对象中的uevent事件,即代码中296-312行。

热插拔(hotplug)是指当有设备插入或拨出系统时,内核可以检测到这种状态变化,并通知用户空间加载或移除该设备对应的驱动程序模块。在Linux系统上内核有两种机制可以通知用户空间执行加载或移除操作,一种是udev,另一种是/sbin/hotplug,在Linux发展的早期,只有/sbin/hotplug,它的幕后推手是内核中的call_usermodehelper函数,它能从内核空间启动一个用户空间程序。随着内核的发展,出现了udev机制并逐渐取代了/sbin/hotplug。udev的实现基于内核中的网络机制,它通过创建标准的socket接口来监听来自内核的网络广播包,并对接收到的包进行分析处理。

至此,kobject_uevent_env函数我们就分析完了,同时,kobject_uevent、kset_register、kset_create_and_add函数也分析完了,我们了解了kset的创建和注册过程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: