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 plaincopy

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;

};
内核里的设备之间是以树状形式组织的,在这种组织架构里比较靠上层的节点可以看作是下层节点的父节点,反映到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"); 

相关内容