700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Linux内核部件分析 设备驱动模型的基石kobject

Linux内核部件分析 设备驱动模型的基石kobject

时间:2022-01-26 17:04:21

相关推荐

Linux内核部件分析  设备驱动模型的基石kobject

之前我们分析了引用计数kref,总结了sysfs提供的API,并翻译了介绍kobject原理及用法的文档。应该说准备工作做得足够多,kobject的实现怎么都可以看懂了,甚至只需要总结下API就行了。可我还是决定把kobject的实现代码从头分析一遍。一是因为kobject的代码很重要,会在设备驱动模型代码中无数次被用到,如果不熟悉的话可以说是举步维艰。二是为了熟悉linux的编码风格,为以后分析更大规模的代码奠定基础。

kobject的头文件在include/linux/kobject.h,实现在lib/kobject.c。闲话少说,上代码。

structkobject{constchar*name;structlist_headentry;structkobject*parent;structkset*kset;structkobj_type*ktype;structsysfs_dirent*sd;structkrefkref;unsignedintstate_initialized:1;unsignedintstate_in_sysfs:1;unsignedintstate_add_uevent_sent:1;unsignedintstate_remove_uevent_sent:1;unsignedintuevent_suppress:1;};

在struct kobject中,name是名字,entry是用于kobject所属kset下的子kobject链表,parent指向kobject的父节点,kset指向kobject所属的kset,ktype定义了kobject所属的类型,sd指向kobject对应的sysfs目录,kref记录kobject的引用计数,之后是一系列标志。

structkobj_type{void(*release)(structkobject*kobj);structsysfs_ops*sysfs_ops;structattribute**default_attrs;};

struct kobj_type就是定义了kobject的公共类型,其中既有操作的函数,也有公共的属性。其中release()是在kobject释放时调用的,sysfs_ops中定义了读写属性文件时调用的函数。default_attrs中定义了这类kobject公共的属性。

structkset{structlist_headlist;spinlock_tlist_lock;structkobjectkobj;structkset_uevent_ops*uevent_ops;};

struct kset可以看成在kobject上的扩展,它包含一个kobject的链表,可以方便地表示sysfs中目录与子目录的关系。其中,list是所属kobject的链表头,list_lock用于在访问链表时加锁,kobj是kset的内部kobject,要表现为sysfs中的目录就必须拥有kobject的功能,最后的kset_uevent_ops定义了对发往用户空间的uevent的处理。我对uevent不了解,会尽量忽略。

structkobj_attribute{structattributeattr;ssize_t(*show)(structkobject*kobj,structkobj_attribute*attr,char*buf);ssize_t(*store)(structkobject*kobj,structkobj_attribute*attr,constchar*buf,size_tcount);};

struct kobj_attribute是kobject在attribute上做出的扩展,添加了两个专门读写kobject属性的函数。无论是kobject,还是kset(说到底是kset内部的kobject),都提供了使用kobj_attribute的快速创建方法。

结构差不多介绍完了,下面看看实现。我所知道的代码分析风格,喜欢自顶向下的方式,从一个函数开始,介绍出一个函数调用树。在代码量很大,涉及调用层次很深的时候,确实要采用这种打洞的方式来寻找突破口。但这种自顶向下的方式有两个问题:一是很容易迷失,二是代码分析的难度会逐渐增大而不是减小。在茫茫的代码中,你一头下去,周围都是你不认识的函数,一个函数里调用了三个陌生的函数,其中一个陌生的函数又调用了五个更陌生的函数...不久你就会产生很强的挫败感。这就像走在沙漠上,你不知道终点在哪,也许翻过一个沙丘就到了,也许还有无数个沙丘。而且在这种分析时,人是逐渐走向细节,容易被细节所困扰,忽略了整体的印象与代码的层次感。所以,我觉得在分析代码时,也可以采用自底向上的方式,从细小的、内部使用的函数,到比较宏观的、供外部调用的函数。而且按照这种顺序来看代码,基本就是文件从头读到尾的顺序,也比较符合写代码的流程。linux代码喜欢在文件开始处攒内部静态函数,攒到一定程度爆发,突然实现几个外部API,然后再攒,再实现。而且之前的内部静态函数会反复调用到。linux代码写得很有层次感,除了内外有别,还把意思相近的,或者功能刚好相反的,或者使用时顺序调用的函数放在一起,很便于阅读。闲话少说,等你看完kobject的实现自然就清楚了。

staticintpopulate_dir(structkobject*kobj){structkobj_type*t=get_ktype(kobj);structattribute*attr;interror=0;inti;if(t&&t->default_attrs){for(i=0;(attr=t->default_attrs[i])!=NULL;i++){error=sysfs_create_file(kobj,attr);if(error)break;}}returnerror;}staticintcreate_dir(structkobject*kobj){interror=0;if(kobject_name(kobj)){error=sysfs_create_dir(kobj);if(!error){error=populate_dir(kobj);if(error)sysfs_remove_dir(kobj);}}returnerror;}

create_dir()在sysfs中创建kobj对应的目录,populate_dir()创建kobj中默认属性对应的文件。create_dir()正是调用populate_dir()实现的。

staticintget_kobj_path_length(structkobject*kobj){intlength=1;structkobject*parent=kobj;/*walkuptheancestorsuntilwehittheonepointingtothe*root.*Add1tostrlenforleading'/'ofeachlevel.*/do{if(kobject_name(parent)==NULL)return0;length+=strlen(kobject_name(parent))+1;parent=parent->parent;}while(parent);returnlength;}staticvoidfill_kobj_path(structkobject*kobj,char*path,intlength){structkobject*parent;--length;for(parent=kobj;parent;parent=parent->parent){intcur=strlen(kobject_name(parent));/*backupenoughtoprintthisnamewith'/'*/length-=cur;strncpy(path+length,kobject_name(parent),cur);*(path+--length)='/';}pr_debug("kobject:'%s'(%p):%s:path='%s'\n",kobject_name(kobj),kobj,__func__,path);}/***kobject_get_path-generateandreturnthepathassociatedwithagivenkobjandksetpair.**@kobj:kobjectinquestion,withwhichtobuildthepath*@gfp_mask:theallocationtypeusedtoallocatethepath**Theresultmustbefreedbythecallerwithkfree().*/char*kobject_get_path(structkobject*kobj,gfp_tgfp_mask){char*path;intlen;len=get_kobj_path_length(kobj);if(len==0)returnNULL;path=kzalloc(len,gfp_mask);if(!path)returnNULL;fill_kobj_path(kobj,path,len);returnpath;}

前面两个是内部函数,get_kobj_path_length()获得kobj路径名的长度,fill_kobj_path()把kobj路径名填充到path缓冲区中。

kobject_get_path()靠两个函数获得kobj的路径名,从攒函数到爆发一气呵成。

staticvoidkobj_kset_join(structkobject*kobj){if(!kobj->kset)return;kset_get(kobj->kset);spin_lock(&kobj->kset->list_lock);list_add_tail(&kobj->entry,&kobj->kset->list);spin_unlock(&kobj->kset->list_lock);}/*removethekobjectfromitskset'slist*/staticvoidkobj_kset_leave(structkobject*kobj){if(!kobj->kset)return;spin_lock(&kobj->kset->list_lock);list_del_init(&kobj->entry);spin_unlock(&kobj->kset->list_lock);kset_put(kobj->kset);}

kobj_kset_join()把kobj加入kobj->kset的链表中,kobj_kset_leave()把kobj从kobj->kset的链表中去除,两者功能相对。

staticvoidkobject_init_internal(structkobject*kobj){if(!kobj)return;kref_init(&kobj->kref);INIT_LIST_HEAD(&kobj->entry);kobj->state_in_sysfs=0;kobj->state_add_uevent_sent=0;kobj->state_remove_uevent_sent=0;kobj->state_initialized=1;}staticintkobject_add_internal(structkobject*kobj){interror=0;structkobject*parent;if(!kobj)return-ENOENT;if(!kobj->name||!kobj->name[0]){WARN(1,"kobject:(%p):attemptedtoberegisteredwithempty""name!\n",kobj);return-EINVAL;}parent=kobject_get(kobj->parent);/*joinksetifset,useitasparentifwedonotalreadyhaveone*/if(kobj->kset){if(!parent)parent=kobject_get(&kobj->kset->kobj);kobj_kset_join(kobj);kobj->parent=parent;}pr_debug("kobject:'%s'(%p):%s:parent:'%s',set:'%s'\n",kobject_name(kobj),kobj,__func__,parent?kobject_name(parent):"<NULL>",kobj->kset?kobject_name(&kobj->kset->kobj):"<NULL>");error=create_dir(kobj);if(error){kobj_kset_leave(kobj);kobject_put(parent);kobj->parent=NULL;/*benoisyonerrorissues*/if(error==-EEXIST)printk(KERN_ERR"%sfailedfor%swith""-EEXIST,don'ttrytoregisterthingswith""thesamenameinthesamedirectory.\n",__func__,kobject_name(kobj));elseprintk(KERN_ERR"%sfailedfor%s(%d)\n",__func__,kobject_name(kobj),error);dump_stack();}elsekobj->state_in_sysfs=1;returnerror;}

kobject_init_internal()初始化kobj。

kobject_add_internal()把kobj加入已有的结构。

这两个函数看似无关,实际很有关系。在kobject中有好几个结构变量,但重要的只有两个,一个是kset,一个是parent。这两个都是表示当前kobject在整个体系中的位置,决不能自行决定,需要外部参与设置。那把kobject创建的过程分为init和add两个阶段也就很好理解了。kobject_init_internal()把一些能自动初始化的结构变量初始化掉,等外界设置了parent和kset,再调用kobject_add_internal()把kobject安在适当的位置,并创建相应的sysfs目录及文件。

intkobject_set_name_vargs(structkobject*kobj,constchar*fmt,va_listvargs){constchar*old_name=kobj->name;char*s;if(kobj->name&&!fmt)return0;kobj->name=kvasprintf(GFP_KERNEL,fmt,vargs);if(!kobj->name)return-ENOMEM;/*ewww...someofthesebuggershave'/'inthename...*/while((s=strchr(kobj->name,'/')))s[0]='!';kfree(old_name);return0;}/***kobject_set_name-Setthenameofakobject*@kobj:structkobjecttosetthenameof*@fmt:formatstringusedtobuildthename**Thissetsthenameofthekobject.Ifyouhavealreadyaddedthe*kobjecttothesystem,youmustcallkobject_rename()inorderto*changethenameofthekobject.*/intkobject_set_name(structkobject*kobj,constchar*fmt,...){va_listvargs;intretval;va_start(vargs,fmt);retval=kobject_set_name_vargs(kobj,fmt,vargs);va_end(vargs);returnretval;}

kobject_set_name()是设置kobj名称的,它又调用kobject_set_name_vargs()实现。但要注意,这个kobject_set_name()仅限于kobject添加到体系之前,因为它只是修改了名字,并未通知用户空间。

voidkobject_init(structkobject*kobj,structkobj_type*ktype){char*err_str;if(!kobj){err_str="invalidkobjectpointer!";gotoerror;}if(!ktype){err_str="musthaveaktypetobeinitializedproperly!\n";gotoerror;}if(kobj->state_initialized){/*donoterroroutassometimeswecanrecover*/printk(KERN_ERR"kobject(%p):triedtoinitaninitialized""object,somethingisseriouslywrong.\n",kobj);dump_stack();}kobject_init_internal(kobj);kobj->ktype=ktype;return;error:printk(KERN_ERR"kobject(%p):%s\n",kobj,err_str);dump_stack();}

kobject_init()就是调用kobject_init_internal()自动初始化了一些结构变量,然后又设置了ktype。其实这个ktype主要是管理一些默认属性什么的,只要在kobject_add_internal()调用create_dir()之前设置就行,之所以会出现在kobject_init()中,完全是为了与后面的kobject_create()相对比。

staticintkobject_add_varg(structkobject*kobj,structkobject*parent,constchar*fmt,va_listvargs){intretval;retval=kobject_set_name_vargs(kobj,fmt,vargs);if(retval){printk(KERN_ERR"kobject:cannotsetnameproperly!\n");returnretval;}kobj->parent=parent;returnkobject_add_internal(kobj);}/***kobject_add-themainkobjectaddfunction*@kobj:thekobjecttoadd*@parent:pointertotheparentofthekobject.*@fmt:formattonamethekobjectwith.**Thekobjectnameissetandaddedtothekobjecthierarchyinthis*function.**If@parentisset,thentheparentofthe@kobjwillbesettoit.*If@parentisNULL,thentheparentofthe@kobjwillbesettothe*kobjectassocitedwiththeksetassignedtothiskobject.Ifnokset*isassignedtothekobject,thenthekobjectwillbelocatedinthe*rootofthesysfstree.**Ifthisfunctionreturnsanerror,kobject_put()mustbecalledto*properlycleanupthememoryassociatedwiththeobject.*Undernoinstanceshouldthekobjectthatispassedtothisfunction*bedirectlyfreedwithacalltokfree(),thatcanleakmemory.**Note,no"add"ueventwillbecreatedwiththiscall,thecallershouldset*upallofthenecessarysysfsfilesfortheobjectandthencall*kobject_uevent()withtheUEVENT_ADDparametertoensurethat*userspaceisproperlynotifiedofthiskobject'screation.*/intkobject_add(structkobject*kobj,structkobject*parent,constchar*fmt,...){va_listargs;intretval;if(!kobj)return-EINVAL;if(!kobj->state_initialized){printk(KERN_ERR"kobject'%s'(%p):triedtoaddan""uninitializedobject,somethingisseriouslywrong.\n",kobject_name(kobj),kobj);dump_stack();return-EINVAL;}va_start(args,fmt);retval=kobject_add_varg(kobj,parent,fmt,args);va_end(args);returnretval;}

kobject_add()把kobj添加到体系中。但它还有一个附加功能,设置kobj的名字。parent也是作为参数传进来的,至于为什么kset没有同样传进来,或许是历史遗留原因吧。

intkobject_init_and_add(structkobject*kobj,structkobj_type*ktype,structkobject*parent,constchar*fmt,...){va_listargs;intretval;kobject_init(kobj,ktype);va_start(args,fmt);retval=kobject_add_varg(kobj,parent,fmt,args);va_end(args);returnretval;}

kobject_init_and_add()虽然是kobject_init()和kobject_add()的合并,但并不常用,因为其中根本没留下设置kset的空挡,这无疑不太合适。

intkobject_rename(structkobject*kobj,constchar*new_name){interror=0;constchar*devpath=NULL;constchar*dup_name=NULL,*name;char*devpath_string=NULL;char*envp[2];kobj=kobject_get(kobj);if(!kobj)return-EINVAL;if(!kobj->parent)return-EINVAL;devpath=kobject_get_path(kobj,GFP_KERNEL);if(!devpath){error=-ENOMEM;gotoout;}devpath_string=kmalloc(strlen(devpath)+15,GFP_KERNEL);if(!devpath_string){error=-ENOMEM;gotoout;}sprintf(devpath_string,"DEVPATH_OLD=%s",devpath);envp[0]=devpath_string;envp[1]=NULL;name=dup_name=kstrdup(new_name,GFP_KERNEL);if(!name){error=-ENOMEM;gotoout;}error=sysfs_rename_dir(kobj,new_name);if(error)gotoout;/*Installthenewkobjectname*/dup_name=kobj->name;kobj->name=name;/*Thisfunctionismostly/onlyusedfornetworkinterface.*Somehotplugpackagetrackinterfacesbytheirnameand*thereforewanttoknowwhenthenameischangedbytheuser.*/kobject_uevent_env(kobj,KOBJ_MOVE,envp);out:kfree(dup_name);kfree(devpath_string);kfree(devpath);kobject_put(kobj);returnerror;}

kobject_rename()就是在kobj已经添加到系统之后,要改名字时调用的函数。它除了完成kobject_set_name()的功能,还向用户空间通知这一消息。

intkobject_move(structkobject*kobj,structkobject*new_parent){interror;structkobject*old_parent;constchar*devpath=NULL;char*devpath_string=NULL;char*envp[2];kobj=kobject_get(kobj);if(!kobj)return-EINVAL;new_parent=kobject_get(new_parent);if(!new_parent){if(kobj->kset)new_parent=kobject_get(&kobj->kset->kobj);}/*oldobjectpath*/devpath=kobject_get_path(kobj,GFP_KERNEL);if(!devpath){error=-ENOMEM;gotoout;}devpath_string=kmalloc(strlen(devpath)+15,GFP_KERNEL);if(!devpath_string){error=-ENOMEM;gotoout;}sprintf(devpath_string,"DEVPATH_OLD=%s",devpath);envp[0]=devpath_string;envp[1]=NULL;error=sysfs_move_dir(kobj,new_parent);if(error)gotoout;old_parent=kobj->parent;kobj->parent=new_parent;new_parent=NULL;kobject_put(old_parent);kobject_uevent_env(kobj,KOBJ_MOVE,envp);out:kobject_put(new_parent);kobject_put(kobj);kfree(devpath_string);kfree(devpath);returnerror;}

kobject_move()则是在kobj添加到系统后,想移动到新的parent kobject下所调用的函数。在通知用户空间上,与kobject_rename()调用的是同一操作。

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;}

kobject_del()仅仅是把kobj从系统中退出,相对于kobject_add()操作。

/***kobject_get-incrementrefcountforobject.*@kobj:object.*/structkobject*kobject_get(structkobject*kobj){if(kobj)kref_get(&kobj->kref);returnkobj;}/**kobject_cleanup-freekobjectresources.*@kobj:objecttocleanup*/staticvoidkobject_cleanup(structkobject*kobj){structkobj_type*t=get_ktype(kobj);constchar*name=kobj->name;pr_debug("kobject:'%s'(%p):%s\n",kobject_name(kobj),kobj,__func__);if(t&&!t->release)pr_debug("kobject:'%s'(%p):doesnothavearelease()""function,itisbrokenandmustbefixed.\n",kobject_name(kobj),kobj);/*send"remove"ifthecallerdidnotdoitbutsent"add"*/if(kobj->state_add_uevent_sent&&!kobj->state_remove_uevent_sent){pr_debug("kobject:'%s'(%p):autocleanup'remove'event\n",kobject_name(kobj),kobj);kobject_uevent(kobj,KOBJ_REMOVE);}/*removefromsysfsifthecallerdidnotdoit*/if(kobj->state_in_sysfs){pr_debug("kobject:'%s'(%p):autocleanupkobject_del\n",kobject_name(kobj),kobj);kobject_del(kobj);}if(t&&t->release){pr_debug("kobject:'%s'(%p):callingktyperelease\n",kobject_name(kobj),kobj);t->release(kobj);}/*freenameifweallocatedit*/if(name){pr_debug("kobject:'%s':freename\n",name);kfree(name);}}staticvoidkobject_release(structkref*kref){kobject_cleanup(container_of(kref,structkobject,kref));}/***kobject_put-decrementrefcountforobject.*@kobj:object.**Decrementtherefcount,andif0,callkobject_cleanup().*/voidkobject_put(structkobject*kobj){if(kobj){if(!kobj->state_initialized)WARN(1,KERN_WARNING"kobject:'%s'(%p):isnot""initialized,yetkobject_put()isbeing""called.\n",kobject_name(kobj),kobj);kref_put(&kobj->kref,kobject_release);}}

kobject_get()和kobject_put()走的完全是引用计数的路线。kobject_put()会在引用计数降为零时撤销整个kobject的存在:向用户空间发生REMOVE消息,从sysfs中删除相应目录,调用kobj_type中定义的release函数,释放name所占的空间。

看看前面介绍的API。

intkobject_set_name(structkobject*kobj,constchar*name,...)__attribute__((format(printf,2,3)));intkobject_set_name_vargs(structkobject*kobj,constchar*fmt,va_listvargs);voidkobject_init(structkobject*kobj,structkobj_type*ktype);int__must_checkkobject_add(structkobject*kobj,structkobject*parent,constchar*fmt,...);int__must_checkkobject_init_and_add(structkobject*kobj,structkobj_type*ktype,structkobject*parent,constchar*fmt,...);voidkobject_del(structkobject*kobj);int__must_checkkobject_rename(structkobject*,constchar*new_name);int__must_checkkobject_move(structkobject*,structkobject*);structkobject*kobject_get(structkobject*kobj);voidkobject_put(structkobject*kobj);char*kobject_get_path(structkobject*kobj,gfp_tflag);

基本上概扩了kobject从创建到删除,包括中间改名字,改位置,以及引用计数的变动。

当然,kobject创建仍比较麻烦,因为ktype需要自己写。下面就是kobject提供的一种快速创建方法。

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;}structsysfs_opskobj_sysfs_ops={.show=kobj_attr_show,.store=kobj_attr_store,};staticvoiddynamic_kobj_release(structkobject*kobj){pr_debug("kobject:(%p):%s\n",kobj,__func__);kfree(kobj);}staticstructkobj_typedynamic_kobj_ktype={.release=dynamic_kobj_release,.sysfs_ops=&kobj_sysfs_ops,};

这个就是kobject自身提供的一种kobj_type,叫做dynamic_kobj_ktype。它没有提供默认的属性,但提供了release函数及访问属性的方法。

structkobject*kobject_create(void){structkobject*kobj;kobj=kzalloc(sizeof(*kobj),GFP_KERNEL);if(!kobj)returnNULL;kobject_init(kobj,&dynamic_kobj_ktype);returnkobj;}structkobject*kobject_create_and_add(constchar*name,structkobject*parent){structkobject*kobj;intretval;kobj=kobject_create();if(!kobj)returnNULL;retval=kobject_add(kobj,parent,"%s",name);if(retval){printk(KERN_WARNING"%s:kobject_adderror:%d\n",__func__,retval);kobject_put(kobj);kobj=NULL;}returnkobj;}

在kobject_create()及kobject_create_add()中,使用了这种dynamic_kobj_ktype。这是一种很好的偷懒方法。因为release()函数会释放kobj,所以这里的kobj必须是kobject_create()动态创建的。这里的kobject_create()和kobject_init()相对,kobject_create_and_add()和kobject_init_and_add()相对。值得一提的是,这里用kobject_create()和kobject_create_and_add()创建的kobject无法嵌入其它结构,是独立的存在,所以用到的地方很少。

voidkset_init(structkset*k){kobject_init_internal(&k->kobj);INIT_LIST_HEAD(&k->list);spin_lock_init(&k->list_lock);}

kset_init()对kset进行初始化。不过它的界限同kobject差不多。

intkset_register(structkset*k){interr;if(!k)return-EINVAL;kset_init(k);err=kobject_add_internal(&k->kobj);if(err)returnerr;kobject_uevent(&k->kobj,KOBJ_ADD);return0;}

kset_register()最大的特点是简单,它只负责把kset中的kobject连入系统,并发布KOBJ_ADD消息。所以在调用它之前,你要先设置好k->kobj.name、k->kobj.parent、k->kobj.kset。

voidkset_unregister(structkset*k){if(!k)return;kobject_put(&k->kobj);}

kset_unregister()只是简单地释放创建时获得的引用计数。使用引用计数就是这么简单。

structkobject*kset_find_obj(structkset*kset,constchar*name){structkobject*k;structkobject*ret=NULL;spin_lock(&kset->list_lock);list_for_each_entry(k,&kset->list,entry){if(kobject_name(k)&&!strcmp(kobject_name(k),name)){ret=kobject_get(k);break;}}spin_unlock(&kset->list_lock);returnret;}

kset_find_obj()从kset的链表中找到名为name的kobject。这纯粹是一个对外的API。

staticvoidkset_release(structkobject*kobj){structkset*kset=container_of(kobj,structkset,kobj);pr_debug("kobject:'%s'(%p):%s\n",kobject_name(kobj),kobj,__func__);kfree(kset);}staticstructkobj_typekset_ktype={.sysfs_ops=&kobj_sysfs_ops,.release=kset_release,};

与kobject相对的,kset也提供了一种kobj_type,叫做kset_ktype。

staticstructkset*kset_create(constchar*name,structkset_uevent_ops*uevent_ops,structkobject*parent_kobj){structkset*kset;intretval;kset=kzalloc(sizeof(*kset),GFP_KERNEL);if(!kset)returnNULL;retval=kobject_set_name(&kset->kobj,name);if(retval){kfree(kset);returnNULL;}kset->uevent_ops=uevent_ops;kset->kobj.parent=parent_kobj;/**Thekobjectofthisksetwillhaveatypeofkset_ktypeandbelongto*noksetitself.Thatwaywecanproperlyfreeitwhenitis*finishedbeingused.*/kset->kobj.ktype=&kset_ktype;kset->kobj.kset=NULL;returnkset;}/***kset_create_and_add-createastructksetdynamicallyandaddittosysfs**@name:thenameforthekset*@uevent_ops:astructkset_uevent_opsforthekset*@parent_kobj:theparentkobjectofthiskset,ifany.**Thisfunctioncreatesaksetstructuredynamicallyandregistersit*withsysfs.Whenyouarefinishedwiththisstructure,call*kset_unregister()andthestructurewillbedynamicallyfreedwhenit*isnolongerbeingused.**Iftheksetwasnotabletobecreated,NULLwillbereturned.*/structkset*kset_create_and_add(constchar*name,structkset_uevent_ops*uevent_ops,structkobject*parent_kobj){structkset*kset;interror;kset=kset_create(name,uevent_ops,parent_kobj);if(!kset)returnNULL;error=kset_register(kset);if(error){kfree(kset);returnNULL;}returnkset;}

kset_create()和kset_create_and_add()就是使用kset_type的快速创建函数。

说实话,使用kobject_create_and_add()的比较少见,但使用 kset_create_and_add()的情形还是见过一些的。比如sysfs中那些顶层的目录,就是单纯的目录,不需要嵌入什么很复杂的结构,用简单的kset_create_and_add()创建就好了。

staticinlineconstchar*kobject_name(conststructkobject*kobj){returnkobj->name;}staticinlinestructkset*to_kset(structkobject*kobj){returnkobj?container_of(kobj,structkset,kobj):NULL;}staticinlinestructkset*kset_get(structkset*k){returnk?to_kset(kobject_get(&k->kobj)):NULL;}staticinlinevoidkset_put(structkset*k){kobject_put(&k->kobj);}staticinlinestructkobj_type*get_ktype(structkobject*kobj){returnkobj->ktype;}

这些是在kobject.h中的内联函数。这里内联函数更多的意思是方便,易于屏蔽内部实现。

以上就是kobject共800余行的代码实现,当然我们忽略了uevent的那部分。

事实证明,自底向上或者顺序的代码分析方法,还是很适合千行左右的代码分析。而且这样分析很全面,容易我们洞察整个模块的意图,从而在理解代码时从较高的抽象角度去看。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。