linux设备模型深探.docx
《linux设备模型深探.docx》由会员分享,可在线阅读,更多相关《linux设备模型深探.docx(53页珍藏版)》请在冰豆网上搜索。
linux设备模型深探
linux设备模型深探
------------------------------------------
------------------------------------------
一:
前言
Linux设备模型是一个极其复杂的结构体系,在编写驱动程序的时候,通常不会用到这方面的东西,但是。
理解这部份内容,对于我们理解linux设备驱动的结构是大有裨益的。
我们不但可以在编写程序程序的时候知其然,亦知其所以然。
又可以学习到一种极其精致的架构设计方法。
由于之前已经详细分析了sysfs文件系统。
所以本节的讨论主要集中在设备模型的底层实现上。
上层的接口,如pci.,usb,网络设备都可以看成是底层的封装。
二:
kobject,kset和ktype
Kobject,kset,kypte这三个结构是设备模型中的下层架构。
模型中的每一个元素都对应一个kobject.kset和ktype可以看成是kobject在层次结构与属性结构方面的扩充。
将三者之间的关系用图的方示描述如下:
如上图所示:
我们知道。
在sysfs中每一个目录都对应一个kobject.这些kobject都有自己的parent。
在没有指定parent的情况下,都会指向它所属的kset->object。
其次,kset也内嵌了kobject.这个kobject又可以指它上一级的parent。
就这样。
构成了一个空间上面的层次关系。
其实,每个对象都有属性。
例如,电源管理,执插拨事性管理等等。
因为大部份的同类设备都有相同的属性,因此将这个属性隔离开来,存放在ktype中。
这样就可以灵活的管理了.记得在分析sysfs的时候。
对于sysfs中的普通文件读写操作都是由kobject->ktype->sysfs_ops来完成的.
经过上面的分析,我们大概了解了kobject.kset与ktype的大概架设与相互之间的关系。
下面我们从linux源代码中的分析来详细研究他们的操作。
三:
kobject,kset和ktype的操作
为了说明kobject的操作,先写一个测试模块,代码如下:
#include
#include
#include
#include
#include
#include
#include
MODULE_AUTHOR("ericxiao");
MODULE_LICENSE("DualBSD/GPL");
voidobj_test_release(structkobject*kobject);
ssize_teric_test_show(structkobject*kobject,structattribute*attr,char*buf);
ssize_teric_test_store(structkobject*kobject,structattribute*attr,constchar*buf,size_tcount);
structattributetest_attr={
.name="eric_xiao",
.mode=S_IRWXUGO,
};
staticstructattribute*def_attrs[]={
&test_attr,
NULL,
};
structsysfs_opsobj_test_sysops=
{
.show=eric_test_show,
.store=eric_test_store,
};
structkobj_typektype=
{
.release=obj_test_release,
.sysfs_ops=&obj_test_sysops,
.default_attrs=def_attrs,
};
voidobj_test_release(structkobject*kobject)
{
printk("eric_test:
release.\n");
}
ssize_teric_test_show(structkobject*kobject,structattribute*attr,char*buf)
{
printk("haveshow.\n");
printk("attrname:
%s.\n",attr->name);
sprintf(buf,"%s\n",attr->name);
returnstrlen(attr->name)+2;
}
ssize_teric_test_store(structkobject*kobject,structattribute*attr,constchar*buf,size_tcount)
{
printk("havestore\n");
printk("write:
%s\n",buf);
returncount;
}
structkobjectkobj;
staticintkobject_test_init()
{
printk("kbojecttestinit.\n");
kobject_init_and_add(&kobj,&ktype,NULL,"eric_test");
return0;
}
staticintkobject_test_exit()
{
printk("kobjecttestexit.\n");
kobject_del(&kobj);
return0;
}
module_init(kobject_test_init);
module_exit(kobject_test_exit);
加载模块之后,会发现,在/sys下多了一个eric_test目录。
该目录下有一个叫eric_xiao的文件。
如下所示:
[root@localhosteric_test]#ls
eric_xiao
用cat察看此文件:
[root@localhosteric_test]#cateric_xiao
eric_xiao
再用echo往里面写点东西;
[root@localhosteric_test]#echo hello>eric_xiao
Dmesg的输出如下:
haveshow.
attrname:
eric_xiao.
havestore
write:
hello
如上所示。
我们看到了kobject的大概建立过程.我们来看一下kobject_init_and_add()的实现。
在这个函数里,包含了对kobject的大部份操作。
intkobject_init_and_add(structkobject*kobj,structkobj_type*ktype,
structkobject*parent,constchar*fmt,...)
{
va_listargs;
intretval;
//初始化kobject
kobject_init(kobj,ktype);
va_start(args,fmt);
//为kobjcet设置名称,在sysfs中建立相关信息
retval=kobject_add_varg(kobj,parent,fmt,args);
va_end(args);
returnretval;
}
上面的流程主要分为两部份。
一部份是kobject的初始化。
在这一部份,它将kobject与给定的ktype关联起来。
初始化kobject中的各项结构。
另一部份是kobject的名称设置。
空间层次关系的设置,具体表现在sysfs文件系统中.
对于第一部份,代码比较简单,这里不再赘述。
跟踪第二部份,也就是kobject_add_varg()的实现.
staticintkobject_add_varg(structkobject*kobj,structkobject*parent,
constchar*fmt,va_listvargs)
{
va_listaq;
intretval;
va_copy(aq,vargs);
//设置kobject的名字。
即kobject的name成员
retval=kobject_set_name_vargs(kobj,fmt,aq);
va_end(aq);
if(retval){
printk(KERN_ERR"kobject:
cannotsetnameproperly!
\n");
returnretval;
}
//设置kobject的parent。
在上面的例子中,我们没有给它指定父结点
kobj->parent=parent;
//在sysfs中添加kobjcet信息
returnkobject_add_internal(kobj);
}
设置好kobject->name后,转入kobject_add_internal()。
在sysfs中创建空间结构.代码如下:
staticintkobject_add_internal(structkobject*kobj)
{
interror=0;
structkobject*parent;
if(!
kobj)
return-ENOENT;
//如果kobject的名字为空.退出
if(!
kobj->name||!
kobj->name[0]){
pr_debug("kobject:
(%p):
attemptedtoberegisteredwithempty"
"name!
\n",kobj);
WARN_ON
(1);
return-EINVAL;
}
//取kobject的父结点
parent=kobject_get(kobj->parent);
//如果kobject的父结点没有指定,就将kset->kobject做为它的父结点
/*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,__FUNCTION__,
parent?
kobject_name(parent):
"",
kobj->kset?
kobject_name(&kobj->kset->kobj):
"");
//在sysfs中创建kobject的相关元素
error=create_dir(kobj);
if(error){
//v如果创建失败。
减少相关的引用计数
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent=NULL;
/*benoisyonerrorissues*/
if(error==-EEXIST)
printk(KERN_ERR"%sfailedfor%swith"
"-EEXIST,don'ttrytoregisterthingswith"
"thesamenameinthesamedirectory.\n",
__FUNCTION__,kobject_name(kobj));
else
printk(KERN_ERR"%sfailedfor%s(%d)\n",
__FUNCTION__,kobject_name(kobj),error);
dump_stack();
}else
//如果创建成功。
将state_in_sysfs建为1。
表示该object已经在sysfs中了
kobj->state_in_sysfs=1;
returnerror;
}
这段代码比较简单,它主要完成kobject父结点的判断和选定,然后再调用create_dir()在sysfs创建相关信息。
该函数代码如下:
staticintcreate_dir(structkobject*kobj)
{
interror=0;
if(kobject_name(kobj)){
//为kobject创建目录
error=sysfs_create_dir(kobj);
if(!
error){
//为kobject->ktype中的属性创建文件
error=populate_dir(kobj);
if(error)
sysfs_remove_dir(kobj);
}
}
returnerror;
}
我们在上面的示例中看到的/sys下的eric_test目录,以及该目录下面的eric_xiao的这个文件就是这里被创建的。
我们先看一下kobject所表示的目录创建过程。
这是在sysfs_create_dir()中完成的。
代码如下:
intsysfs_create_dir(structkobject*kobj)
{
structsysfs_dirent*parent_sd,*sd;
interror=0;
BUG_ON(!
kobj);
/*如果kobject的parnet存在。
就在目录点的目录下创建这个目录。
如果没有父结点不存在,就在/sys下面创建结点。
在上面的流程中,我们可能并没有为其指定父结点,也没有为其指定kset。
*/
if(kobj->parent)
parent_sd=kobj->parent->sd;
else
parent_sd=&sysfs_root;
//在sysfs中创建目录
error=create_dir(kobj,parent_sd,kobject_name(kobj),&sd);
if(!
error)
kobj->sd=sd;
returnerror;
}
在这里,我们就要联系之前分析过的sysfs文件系统的研究了。
如果不太清楚的,可以在找到那篇文章仔细的研读一下。
create_dir()就是在sysfs中创建目录的接口,在之前已经详细分析过了。
这里不再讲述。
接着看为kobject->ktype中的属性创建文件。
这是在populate_dir()中完成的。
代码如下:
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;
}
这段代码比较简单。
它遍历ktype中的属性。
然后为其建立文件。
请注意:
文件的操作最后都会回溯到ktype->sysfs_ops的show和store这两个函数中.
Kobject的创建已经分析完了,接着分析怎么将一个kobject注销掉。
注意过程是在kobject_del()中完成的。
代码如下:
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;
}
该函数会将在sysfs中的kobject对应的目录删除。
请注意,属性文件是建立在这个目录下面的。
只需要将这个目录删除。
属性文件也随之删除。
是后,减少相关的引用计数,如果kobject的引用计数为零。
则将其所占空间释放.
Kset的操作与kobject类似,因为kset中内嵌了一个kobject结构,所以,大部份操作都是集中在kset->kobject上.具体分析一下kset_create_and_add()这个接口,类似上面分析的kobject接口,这个接口也包括了kset的大部分操作.代码如下:
structkset*kset_create_and_add(constchar*name,
structkset_uevent_ops*uevent_ops,
structkobject*parent_kobj)
{
structkset*kset;
interror;
//创建一个kset
kset=kset_create(name,uevent_ops,parent_kobj);
if(!
kset)
returnNULL;
//注册kset
error=kset_register(kset);
if(error){
//如果注册失败,释放kset
kfree(kset);
returnNULL;
}
returnkset;
}
Kset_create()用来创建一个structkset结构.代码如下:
staticstructkset*kset_create(constchar*name,
structkset_uevent_ops*uevent_ops,
structkobject*parent_kobj)
{
structkset*kset;
kset=kzalloc(sizeof(*kset),GFP_KERNEL);
if(!
kset)
returnNULL;
kobject_set_name(&kset->kobj,name);
kset->uevent_ops=uevent_ops;
kset->kobj.parent=parent_kobj;
kset->kobj.ktype=&kset_ktype;
kset->kobj.kset=NULL;
returnkset;
}
我们注意,在这里创建kset时.为其内嵌的kobject指定其ktype结构为kset_ktype.这个结构的定义如下:
staticstructkobj_typekset_ktype={
.sysfs_ops =&kobj_sysfs_ops,
.release=kset_release,
};
属性文件的读写操作全部都包含在sysfs_ops成员里.kobj_sysfs_ops的定义如下:
structsysfs_opskobj_sysfs_ops={
.show =kobj_attr_show,
.store =kobj_attr_store,
};
Show,store成员对应的函数代码如下所示:
staticssize_tkobj_attr_show(structkobject*kobj,structattribut