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

Linux设备模型分析之kobject

2012-11-21 10:08 246 查看
作者:刘昊昱

博客:http://blog.csdn.net/liuhaoyutz

内核版本:2.6.36


一、kobject应用举例

Linux设备模型最基本的组成元素是kobject,我们先来看一个kobject的应用例子,该程序在Ubuntu10.10,2.6.32-38-generic-pae内核上调试通过。

[cpp]viewplaincopy#include<linux/device.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/string.h>
#include<linux/sysfs.h>
#include<linux/stat.h>

MODULE_AUTHOR("haoyu");
MODULE_LICENSE("DualBSD/GPL");

structmy_kobject
{
intvalue;
structkobjectkobj;
};

structmy_kobjectmy_kobj;

voidkobject_release(structkobject*kobject);
ssize_tkobject_attr_show(structkobject*kobject,structattribute*attr,char*buf);
ssize_tkobject_attr_store(structkobject*kobject,structattribute*attr,constchar*buf,size_tcount);

structattributekobject_attr1={
.name="name",
.mode=S_IRWXUGO,
};

structattributekobject_attr2={
.name="value",
.mode=S_IRWXUGO,
};

staticstructattribute*kobject_def_attrs[]={
&kobject_attr1,
&kobject_attr2,
NULL,
};

structsysfs_opskobject_sysfs_ops=
{
.show=kobject_attr_show,
.store=kobject_attr_store,
};

structkobj_typektype=
{
.release=kobject_release,
.sysfs_ops=&kobject_sysfs_ops,
.default_attrs=kobject_def_attrs,
};

voidkobject_release(structkobject*kobject)
{
printk("kobjectrelease.\n");
}

ssize_tkobject_attr_show(structkobject*kobject,structattribute*attr,char*buf)
{
intcount=0;
structmy_kobject*my_kobj=container_of(kobject,structmy_kobject,kobj);
printk("kobjectattributeshow.\n");
if(strcmp(attr->name,"name")==0)
count=sprintf(buf,"%s\n",kobject->name);
elseif(strcmp(attr->name,"value")==0)
count=sprintf(buf,"%d\n",my_kobj->value);
else
printk("nothisattribute.\n");

returncount;
}

ssize_tkobject_attr_store(structkobject*kobject,structattribute*attr,constchar*buf,size_tcount)
{
intval;
structmy_kobject*my_kobj=container_of(kobject,structmy_kobject,kobj);
printk("kobjectattributestore.\n");
if(strcmp(attr->name,"name")==0)
printk("Cannotchangename.\n");
elseif(strcmp(attr->name,"value")==0)
{
val=buf[0]-'0';
if(val==0||val==1)
my_kobj->value=val;
else
printk("valueis'0'or'1'\n");
}
else
printk("nothisattribute.\n");

returncount;
}

staticintkobject_test_init(void)
{
printk("kbojecttestinit.\n");
kobject_init_and_add(&my_kobj.kobj,&ktype,NULL,"kobject_test");
return0;
}

staticvoidkobject_test_exit(void)
{
printk("kobjecttestexit.\n");
kobject_del(&my_kobj.kobj);
}

module_init(kobject_test_init);
module_exit(kobject_test_exit);


该模块执行过程如下图所示:





二、相关数据结构:
kobject是Linux设备模型中最基本的数据结构,代表设备模式的一个基本对象。
kobj_type是kobject的类型,包括kobject的属性以及属性的操作接口,不同的kobject可以具有相同的kobj_type。
kset是几个kobject的集合,这些kobject可以具有相同的kobj_type,也可以具有不同的kobj_type。
[cpp]viewplaincopystructkobject{
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;
};

/**
*structkset-asetofkobjectsofaspecifictype,belongingtoaspecificsubsystem.
*
*Aksetdefinesagroupofkobjects.Theycanbeindividually
*different"types"butoverallthesekobjectsallwanttobegrouped
*togetherandoperatedoninthesamemanner.ksetsareusedto
*definetheattributecallbacksandothercommoneventsthathappento
*akobject.
*
*@list:thelistofallkobjectsforthiskset
*@list_lock:alockforiteratingoverthekobjects
*@kobj:theembeddedkobjectforthiskset(recursion,isn'titfun...)
*@uevent_ops:thesetofueventoperationsforthiskset.Theseare
*calledwheneverakobjecthassomethinghappentoitsothatthekset
*canaddnewenvironmentvariables,orfilterouttheueventsifso
*desired.
*/
structkset{
structlist_headlist;
spinlock_tlist_lock;
structkobjectkobj;
conststructkset_uevent_ops*uevent_ops;
};

structkset_uevent_ops{
int(*constfilter)(structkset*kset,structkobject*kobj);
constchar*(*constname)(structkset*kset,structkobject*kobj);
int(*constuevent)(structkset*kset,structkobject*kobj,
structkobj_uevent_env*env);
};

structkobj_type{
void(*release)(structkobject*kobj);
conststructsysfs_ops*sysfs_ops;
structattribute**default_attrs;
conststructkobj_ns_type_operations*(*child_ns_type)(structkobject*kobj);
constvoid*(*namespace)(structkobject*kobj);
};

structsysfs_ops{
ssize_t(*show)(structkobject*,structattribute*,char*);
ssize_t(*store)(structkobject*,structattribute*,constchar*,size_t);
};

structattribute{
constchar*name;
mode_tmode;
#ifdefCONFIG_DEBUG_LOCK_ALLOC
structlock_class_key*key;
structlock_class_keyskey;
#endif
};

/*
*Callbackssosysfscandeterminenamespaces
*@current_ns:returncallingtask'snamespace
*@netlink_ns:returnnamespacetowhichasockbelongs(right?)
*@initial_ns:returntheinitialnamespace(i.e.init_net_ns)
*/
structkobj_ns_type_operations{
enumkobj_ns_typetype;
constvoid*(*current_ns)(void);
constvoid*(*netlink_ns)(structsock*sk);
constvoid*(*initial_ns)(void);
};

structkref{
atomic_trefcount;
};

三、kobject注册和注销过程分析
*************************************************************************************
函数的关系图是tynew所加,个人喜欢从宏观上把握函数的实现,如果想看详细的实现,请继续浏览



*************************************************************************************
kobject的注册是通过调用kobject_init_and_add函数,该函数定义如下:
[cpp]viewplaincopy/**
*kobject_init_and_add-initializeakobjectstructureandaddittothekobjecthierarchy
*@kobj:pointertothekobjecttoinitialize
*@ktype:pointertothektypeforthiskobject.
*@parent:pointertotheparentofthiskobject.
*@fmt:thenameofthekobject.
*
*Thisfunctioncombinesthecalltokobject_init()and
*kobject_add().Thesametypeoferrorhandlingafteracallto
*kobject_add()andkobjectlifetimerulesarethesamehere.
*/
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函数对kobject对象进行基本的初始化。然后,调用kobject_add_varg函数将kobject注册到系统中。va_start和va_end是处理可变参数的固定语法。
先来看kobject_init,该函数定义如下:
[cpp]viewplaincopy/**
*kobject_init-initializeakobjectstructure
*@kobj:pointertothekobjecttoinitialize
*@ktype:pointertothektypeforthiskobject.
*
*Thisfunctionwillproperlyinitializeakobjectsuchthatitcanthen
*bepassedtothekobject_add()call.
*
*Afterthisfunctioniscalled,thekobjectMUSTbecleanedupbyacall
*tokobject_put(),notbyacalltokfreedirectlytoensurethatallof
*thememoryiscleanedupproperly.
*/
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();
}

该函数首先确保kobj和ktype都存在,否则直接退出。如果该kobj进行过初始化,则打印警告信息。然后调用kobject_init_internal真正开始初始化kobj,最后把kobj->ktype设置为ktype。
kobject_init_internal函数定义如下:
[cpp]viewplaincopystaticvoidkobject_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;
}

首先初始化kobj->kref,实际上kobj->kref就是一个原子变量(atomic_t)。接着初始化链表项kobj->entry,并设置其他kobject成员。
至此,kobject_init函数就分析完了,我们返回到kobject_init_and_add函数,下面该分析kobject_add_varg函数了:
[cpp]viewplaincopystaticintkobject_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_set_name_vargs设置kob->name。然后初始化kobj->parent为parent参数指定的kobject。最后,调用kobject_add_internal将kobject注册到系统中,该函数定义如下:
[cpp]viewplaincopystaticintkobject_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));
else
printk(KERN_ERR"%sfailedfor%s(%d)\n",
__func__,kobject_name(kobj),error);
dump_stack();
}else
kobj->state_in_sysfs=1;

returnerror;
}

首先确保kobj->name已经被赋值,即kobject必须有名字。如果指定了kobj->kset,则调用kobj_kset_join将kobj加入到kobj->kset中。同时,如果kobj->parent仍为NULL,则将kobj->parent设置为kobj->kset->kobj。
然后,调用create_dir(kobj)在/sys目录下建立kobject相关目录结构。
kobj_kset_join函数定义如下:
[cpp]viewplaincopy/*addthekobjecttoitskset'slist*/
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);
}

create_dir函数定义如下:
[cpp]viewplaincopystaticintcreate_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;
}

首先调用sysfs_create_dir在/sys下建立目录,然后再调用populate_dir在新建目录下生成属性文件。
sysfs_create_dir函数定义如下:
[cpp]viewplaincopy/**
*sysfs_create_dir-createadirectoryforanobject.
*@kobj:objectwe'recreatingdirectoryfor.
*/
intsysfs_create_dir(structkobject*kobj)
{
enumkobj_ns_typetype;
structsysfs_dirent*parent_sd,*sd;
constvoid*ns=NULL;
interror=0;

BUG_ON(!kobj);

if(kobj->parent)
parent_sd=kobj->parent->sd;
else
parent_sd=&sysfs_root;

if(sysfs_ns_type(parent_sd))
ns=kobj->ktype->namespace(kobj);
type=sysfs_read_ns_type(kobj);

error=create_dir(kobj,parent_sd,type,ns,kobject_name(kobj),&sd);
if(!error)
kobj->sd=sd;
returnerror;
}

这里主要是通过调用create_dir函数建立kobject对应的目录。这个函数我们就不继续向下跟踪了。
下面来看populate_dir函数,其定义如下:
[cpp]viewplaincopy/*
*populate_dir-populatedirectorywithattributes.
*@kobj:objectwe'reworkingon.
*
*Mostsubsystemshaveasetofdefaultattributesthatareassociated
*withanobjectthatregisterswiththem.Thisisahelpercalledduring
*objectregistrationthatloopsthroughthedefaultattributesofthe
*subsystemandcreatesattributesfilesfortheminsysfs.
*/
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;
}

该函数循环遍历kobject的所有属性,并调用sysfs_create_file函数在/sys系统对应目录下建立属性文件。
至此,kobject的注册过程我们就分析完了。
kobject的注销过程是调用kobject_del函数,该函数定义如下:
[cpp]viewplaincopy/**
*kobject_del-unlinkkobjectfromhierarchy.
*@kobj:object.
*/
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;
}

这里需要注意的是,只要把目录删除,目录下的属性文件自动就删除了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: