Linux 设备模型之 (kobject、kset 和 Subsystem)(二),kobjectkset
Linux 设备模型之 (kobject、kset 和 Subsystem)(二),kobjectkset
1、kobject 结构
在Linux内核里,kobject是组成Linux设备模型的基础,一个kobject对应sysfs里的一个目录。从面向对象的角度来说,kobject可以看作是所有设备对象的基类,因为C语言并没有面向对象的语法,所以一般是把kobject内嵌到其他结构体里来实现类似的作用,这里的其他结构体可以看作是kobject的派生类。Kobject为Linux设备模型提供了很多有用的功能,比如引用计数,接口抽象,父子关系等等。引用计数本质上就是利用kref实现的。
另外,Linux设备模型还有一个重要的数据结构kset。Kset本身也是一个kobject,所以它在sysfs里同样表现为一个目录,但它和kobject的不同之处在于kset可以看作是一个容器,如果你把它类比为C++里的容器类如list也无不可。Kset之所以能作为容器来使用,其内部正是内嵌了一个双向链表结构struct list_head。
kobject 在内核中的描述
view plaincopystructkobject{
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;
};
内核里的设备之间是以树状形式组织的,在这种组织架构里比较靠上层的节点可以看作是下层节点的父节点,反映到sysfs里就是上级目录和下级目录之间的关系,在内核里,正是kobject帮助我们实现这种父子关系。在kobject的定义里,name表示的是kobject在sysfs中的名字;指针parent用来指向kobject的父对象;Kref大家应该比较熟悉了,kobject通过它来实现引用计数;Kset指针用来指向这个kobject所属的kset,下文会再详细描述kset的用法;对于ktype,如果只是望文生义的话,应该是用来描述kobject的类型信息。Ktype的定义如下:
view plaincopy
structkobj_type{
void(*release)(structkobject*kobj);
conststructsysfs_ops*sysfs_ops;
structattribute**default_attrs;
};函数指针release是给kref使用的,当引用计数为0这个指针指向的函数会被调用来释放内存。sysfs_ops和attribute是做什么用的呢?前文里提到,一个kobject对应sysfs里的一个目录,而目录下的文件就是由sysfs_ops和attribute来实现的,其中,attribute定义了kobject的属性,在sysfs里对应一个文件,sysfs_ops用来定义读写这个文件的方法。Ktype里的attribute是默认的属性,另外也可以使用更加灵活的手段,本文的重点还是放在default attribute。
view plaincopy
#include
#include
#include
#include
structmy_kobj{//内嵌kobject的结构
intval;
structkobjectkobj;
};
structmy_kobj*obj1,*obj2;
structkobj_typemy_type;
structattributename_attr={
.name="name",//文件名
.mode=0444,//指定文件的访问权限
};
structattributeval_attr={
.name="val",//文件名
.mode=0666,//指定文件的访问权限
};
structattribute*my_attrs[]={
&name_attr,
&val_attr,
NULL,
};
/*
结构体structattribute里的name变量用来指定文件名,mode变量用来指定文件的访问权限。
这里需要着重指出的是,数组my_attrs的最后一项一定要赋为NULL,否则会造成内核oops。
*/
ssize_tmy_show(structkobject*kobj,structattribute*attr,char*buffer)
{
structmy_kobj*obj=container_of(kobj,structmy_kobj,kobj);
ssize_tcount=0;
if(strcmp(attr->name,"name")==0){
count=sprintf(buffer,"%s\n",kobject_name(kobj));
}elseif(strcmp(attr->name,"val")==0){
count=sprintf(buffer,"%d\n",obj->val);
}
returncount;
}
ssize_tmy_store(structkobject*kobj,structattribute*attr,constchar*buffer,size_tsize)
{
structmy_kobj*obj=container_of(kobj,structmy_kobj,kobj);
if(strcmp(attr->name,"val")==0){
sscanf(buffer,"%d",&obj->val);
}
returnsize;
}
structsysfs_opsmy_sysfsops={
.show=my_show,
.store=my_store,
};
voidobj_release(structkobject*kobj)
{
structmy_kobj*obj=container_of(kobj,structmy_kobj,kobj);
printk(KERN_INFO"obj_release%s\n",kobject_name(&obj->kobj));
kfree(obj);
}
staticint__initmykobj_init(void)
{
printk(KERN_INFO"mykobj_init\n");
obj1=kzalloc(sizeof(structmy_kobj),GFP_KERNEL);//分配obj1和obj2并赋值
if(!obj1){
return-ENOMEM;
}
obj1->val=1;
obj2=kzalloc(sizeof(structmy_kobj),GFP_KERNEL);
if(!obj2){
kfree(obj1);
return-ENOMEM;
}
obj2->val=2;
my_type.release=obj_release;
my_type.default_attrs=my_attrs;
my_type.sysfs_ops=&my_sysfsops;
kobject_init_and_add(&obj1->kobj,&my_type,NULL,"mykobj1");/*函数来初始化kobject并把它加入到设备模型的体系架构*/
kobject_init_and_add(&obj2->kobj,&my_type,&obj1->kobj,"mykobj2");
/*
kobject_init用来初始化kobject结构,kobject_add用来把kobj加入到设备模型之中。
在实作中,我们先对obj1进行初始化和添加的动作,调用参数里,parent被赋为NULL,表示obj1没有父对象,反映到sysfs里,
my_kobj1的目录会出现在/sys下,obj2的父对象设定为obj1,那么my_kobj2的目录会出现在/sys/my_kobj1下面。
前面提到,kobject也提供了引用计数的功能,虽然本质上是利用kref,但也提供了另外的接口供用户使用。
kobject_init_and_add和kobject_init这两个函数被调用后,kobj的引用计数会初始化为1,
所以在module_exit时要记得用kobject_put来释放引用计数。
*/
return0;
}
staticvoid__exitmykobj_exit(void)
{
printk(KERN_INFO"mykobj_exit\n");
kobject_del(&obj2->kobj);/*先子对象,后父对象*/
kobject_put(&obj2->kobj);
kobject_del(&obj1->kobj);
kobject_put(&obj1->kobj);
return;
}
/*
kobject_del的作用是把kobject从设备模型的那棵树里摘掉,同时sysfs里相应的目录也会删除。
这里需要指出的是,释放的顺序应该是先子对象,后父对象。
因为kobject_init_and_add和kobject_add这两个函数会调用kobject_get来增加父对象的引用计数,
所以kobject_del需要调用kobject_put来减少父对象的引用计数。在本例中,如果先通过kobject_put来释放obj1,
那kobject_del(&obj2->kobj)就会出现内存错误。
*/
module_init(mykobj_init);
module_exit(mykobj_exit);
MODULE_LICENSE("GPL");
评论暂时关闭