> Linux教程 > linux基础 >

linux设备模型之kobject

Linux驱动模型是整个linux设备驱动的基石。一般来说,驱动工程师可以忽略设备模型,因为底下的设备模型可以处理好复杂的device、driver、bus以及class等等的关系。了解linux的模块机制,了解file的read、write和ioctl,了解register_chrdev、misc_register等函数,可能我们也可以写出驱动。但是经常有这样那样的疑惑:设备和驱动是如何结合的?谁做的媒人?sysfs文件系统是如何实现的?这个内存文件系统存在是为了什么?

 

正如ldd3中所讲,设备模型的复杂性使得从较高的层次来理解是相对困难的,但是了解设备模型是大有裨益的。就我目前的理解:裨益有二:一是视野变高,我们可以从较高的层次来看待设备驱动,相当于掌握了一种方法,一种机制,再去看各个驱动模型的子系统(iic,spi等等)就会有一览众山小的感觉。举个例子,打仗是什么?1v1是单挑,10v10是群殴,10wVS10w就是打仗了。那兵家的祖师孙子怎么说:《孙子兵法》开篇是“兵者,国之大事,死生之地,存亡之道,不可不察也”。看了这句话,再看后面的各种计谋就有了不一样的心态和思路。张无忌背出七伤拳总纲时,就把那几个老头吓住了。所以说先搞懂(或者说有一定理解)Devicemodel,对后面的驱动学习起来总纲的作用。说了这么多废话,那裨益之二就是掌握面向对象设计的思想,面向对象?c可以吗?其实是可以的,虽然c语言中不允许直接描述继承关系,但是在一个结构中嵌入另外一个结构的技术类似于类的继承。linuxkernel中很多地方用到了面向对象设计的思想:比如现在讨论的设备模型以及linux的vfs文件系统。

 

在分析之前,我们可以看下Documentation\driver-model的文档,这里只提一下overview.txt:

这里我就不翻译了,英语能力有限的仅。。。

TheLinux Kernel Driver Model is a unification of all the disparatedriver

modelsthat were previously used in the kernel.

Traditionaldriver models implemented some sort of tree-likestructure

(sometimesjust a list) for the devices they control.

UserInterface

Byvirtue of having a complete hierarchical view of all the devices inthe

system,exporting a complete hierarchical view to userspace becomesrelatively

easy.This has been accomplished by implementing a special purpose virtual

filesystem named sysfs.

 

既然Devicemodel是很复杂的,意味着他分层了。下层的砖瓦就是kobject、kobj_type、kset,中层的预制板就是device,bus和driver,上层的地板天花板就是pci_device等等。

 

万丈高楼平地起,我们先玩玩砖瓦,和和稀泥。

 

先给出kobject、kset和kobj_type的数据结构:

 

structkobject {

constchar *name;

structlist_head entry;

structkobject *parent;

structkset *kset;

structkobj_type *ktype;

structsysfs_dirent *sd;

structkref kref;

unsignedint state_initialized:1;

unsignedint state_in_sysfs:1;

unsignedint state_add_uevent_sent:1;

unsignedint state_remove_uevent_sent:1;

unsignedint uevent_suppress:1;

};

 

structkset {

structlist_head list;

spinlock_tlist_lock;

structkobject kobj;

conststruct kset_uevent_ops *uevent_ops;

};

 

structkobj_type {

void(*release)(struct kobject *kobj);

conststruct sysfs_ops *sysfs_ops;

structattribute **default_attrs;

};

 

初步一看,kobject结构体有成员:structkset *kset;和structkobj_type *ktype;,他关系广点,我们先来看他。

在看kobject和kset之前,我们可以再看看linux-2.6.34.1\Documentation\kobject.txt

 

Partof the difficulty in understanding the driver model - and the kobject

abstractionupon which it is built - is that there is no obvious starting

place.Dealing with kobjects requires understanding a few different types,

allof which make reference to each other.

 

-A kobject is an object of type struct kobject. Kobjects have a name

anda reference count. A kobject also has aparent pointer (allowing

objectsto be arranged into hierarchies), a specifictype, and,

usually,a representation in the sysfs virtual filesystem.

 

Kobjectsare generally not interesting on their own; instead, they are

usuallyembedded within some other structure which contains the stuff

thecode is really interested in.

 

Nostructure should EVER have more than one kobject embedded within it.

Ifit does, the reference counting for the object is sure to be messed

upand incorrect, and your code will be buggy. So do not do this.

 

-A ktype is the type of object that embeds a kobject. Every structure

thatembeds a kobject needs a corresponding ktype. The ktype controls

whathappens to the kobject when it is created and destroyed.

 

-A kset is a group of kobjects. These kobjects can be of the samektype

orbelong to different ktypes. The kset is the basic container type for

collectionsof kobjects. Ksets contain their own kobjects, but you can

safelyignore that implementation detail as the kset core code handles

thiskobject automatically.

 

linux-2.6.34.1\linux-2.6.34.1\samples\kobject目录下专门有两个示例来帮助我们研究kobject和kset,将其编译成模块,然后一步步kgdb。

 

Insmodkobject-example.komodule_init(example_init)

 

在之前,最好先了解一下操作kobject的一些函数方法,在kobject.c中:

 

kobject_init_internal

staticvoid kobject_init_internal(struct kobject *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为1;初始化kobj->entry双向链表;

初始化state_in_sysfs|state_add_uevent_sent|state_remove_uevent_sent|state_initialized这几个状态值。

 

kobject_init 这个函数是在kobject_init_internal函数上多做了一点事

voidkobject_init(struct kobject *kobj, struct kobj_type *ktype)

{

char*err_str;

 

if(!kobj) {

err_str= "invalid kobject pointer!";

gotoerror;

}

if(!ktype) {

err_str= "must have a ktype to be initialized properly!\n";

gotoerror;

}

if(kobj->state_initialized) {

/*do not error out as sometimes we can recover */

printk(KERN_ERR"kobject (%p): tried to init an initialized "

"object, something is seriously wrong.\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();

}

这个函数的第二个参数就是structkobj_type *ktype,多做的那点事就与这个相关:

kobj->ktype= ktype;

设定kobj的ktype类型。

 

kobject_create 这个函数又是在kobject_init基础之上的

structkobject *kobject_create(void)

{

structkobject *kobj;

 

kobj= kzalloc(sizeof(*kobj), GFP_KERNEL);

if(!kobj)

returnNULL;

 

kobject_init(kobj,&dynamic_kobj_ktype);

returnkobj;

}

动态创建一个kobject结构体,kobj的ktype是dynamic_kobj_ktype。

 

kobject_set_name_vargs设置kobject的name

intkobject_set_name_vargs(struct kobject *kobj, const char *fmt,

va_list vargs)

{

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... some of these buggers have '/' in the name ... */

while((s = strchr(kobj->name, '/')))

s[0]= '!';

 

kfree(old_name);

return0;

}

 

kobject_add_internal//添加kobject结构体

staticint kobject_add_internal(struct kobject *kobj)

{

interror = 0;

structkobject *parent;

 

if(!kobj)

return-ENOENT;

 

if(!kobj->name || !kobj->name[0]) {

WARN(1,"kobject: (%p): attempted to be registered with empty "

"name!\n", kobj);

return-EINVAL;

}

 

parent= kobject_get(kobj->parent);

 

/*join kset if set, use it as parent if we do not already have one */

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;

 

/*be noisy on error issues */

if(error == -EEXIST)

printk(KERN_ERR"%s failed for %s with "

"-EEXIST, don't try to register things with "

"the same name in the same directory.\n",

__func__, kobject_name(kobj));

else

printk(KERN_ERR"%s failed for %s (%d)\n",

__func__, kobject_name(kobj), error);

dump_stack();

}else

kobj->state_in_sysfs= 1;

 

returnerror;

}

这个函数我们分开看下面几点:

  1. parent= kobject_get(kobj->parent);

找到kobj的parent,如果没有,则返回NULL;并增加kobj的引用计数。

2、 if(kobj->kset) {

if(!parent)

parent= kobject_get(&kobj->kset->kobj);

kobj_kset_join(kobj);

kobj->parent= parent;

}

如果kobj->kset已被设置,如果parent为NULL,

parent= kobject_get(&kobj->kset->kobj),我们把kobj->kset->kobj返回当作parent;

kobj_kset_join 将kobject加入它的kset中的链表

staticvoid kobj_kset_join(struct kobject *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);

}

  1. error= create_dir(kobj); 这个函数就与sysfs有关系了,在sysfs文件系统中创建目录

具体如何创建的,我们暂且不管吧。创建成功的话,将kobj->state_in_sysfs设置为1.

 

kobject_add_varg

这个函数就是kobject_set_name_vargs和kobject_add_internal的组合

staticint kobject_add_varg(struct kobject *kobj, struct kobject *parent,

const char *fmt, va_list vargs)

{

intretval;

 

retval= kobject_set_name_vargs(kobj, fmt, vargs);

if(retval) {

printk(KERN_ERR"kobject: can not set name properly!\n");

returnretval;

}

kobj->parent= parent;

returnkobject_add_internal(kobj);

}

 

而kobject_add就是在kobject_add_varg上面的函数,基本上差不多。

 

看完了kobject的基本操作函数,再来一步步分析kobject_example.ko的注册:

 

 

 

staticint __init example_init(void)

{

intretval;

 

/*

* Create a simple kobject with the name of "kobject_example",

* located under /sys/kernel/

*

* As this is a simple directory, no uevent will be sent to

* userspace. That is why this function should not be used for

* any type of dynamic kobjects, where the name and number are

* not known ahead of time.

*/

example_kobj= kobject_create_and_add("kobject_example", kernel_kobj); -----a------

if(!example_kobj)

return-ENOMEM;

 

/*Create the files associated with this kobject */

retval= sysfs_create_group(example_kobj, &attr_group); ------b------

if(retval)

kobject_put(example_kobj);

 

returnretval;

}

 

 

  1. example_kobj= kobject_create_and_add("kobject_example", kernel_kobj);

kernel_kobj是什么?它也是个kobject,对应于/sys/kernel目录,在ksysfs_init函数中创建。这里实际上是在/sys/kernel目录下添加了kobject_example目录。

 

我们具体看下kobject_create_and_add函数

/*create a struct kobject dynamically and register it with sysfs */

 

struct kobject*kobject_create_and_add(const char *name, struct kobject *parent)

{

struct kobject *kobj;

int retval;

 

kobj = kobject_create(); ------a1-------

if (!kobj)

return NULL;

 

retval = kobject_add(kobj,parent, "%s", name); ------a2------

if (retval) {

printk(KERN_WARNING "%s:kobject_add error: %d\n",

__func__, retval);

kobject_put(kobj);

kobj = NULL;

}

return kobj;

}

 

 

A1、创建一个kobject

A2、添加kobject

 

 

  1. sysfs_create_group(example_kobj,&attr_group);

先看下attr_group是什么

staticstruct attribute_group attr_group = {

.attrs= attrs,

};

 

staticstruct attribute *attrs[] = {

&foo_attribute.attr,

&baz_attribute.attr,

&bar_attribute.attr,

NULL, /*need to NULL terminate the list of attributes */

};

 

staticstruct kobj_attribute foo_attribute =

__ATTR(foo,0666, foo_show, foo_store);

staticstruct kobj_attribute baz_attribute =

__ATTR(baz,0666, b_show, b_store);

staticstruct kobj_attribute bar_attribute =

__ATTR(bar,0666, b_show, b_store);

 

__ATTR的宏定义如下:

#define__ATTR(_name,_mode,_show,_store) { \

.attr= {.name = __stringify(_name), .mode = _mode }, \

.show =_show, \

.store =_store, \

}

 

sysfs_create_group又和sysfs相关,我们给出这个函数的效果:在/sys/kernel/kobject_example目录下建立foo、baz和bar三个文件。

 

可以对三个文件进行读写操作,读写操作分别调用各自的show和store函数。

(责任编辑:IT)