本文简单介绍构成Linux设备驱动模型的最底层的数据结构:kobject、kset和kobj_type等等。kobject是组成设备模型的基本结构,在sysfs中显示的每一个对象,都对应着一个kobject,而kset是一组具有某种相似性的kobject的合集。每个kobject都需要一个相应的kobj_type结构。
一、kobject
不多废话,首先看kobject的定义( <linux/kobject.h> ):
struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
struct sysfs_dirent *sd;
struct kref kref;
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};
捡几个相对来说比较重要的成员来说明下:
@name:kobject对象的名字,显示在sysfs下
@entry:内核用该字段将属于相同kset的不同的kobject链接起来
@parent:kobject层次依靠此字段得以实现,parent指向当前kobject的上一层
@kset:指向所属的kset
@ktype:指向kobject的类型,每个kobject都必须有一个类型
@kref:对象的引用计数依靠此字段得以实现
1、kobject的初始化
一个结构体只有被正确地初始化之后才能正确地使用它。kobject也不例外,要使用kobject之前,必须对其进行初始化,内核提供kobject_init函数来初始化kobject:
void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
该函数初始化设置kobject的引用计数为1、将kobject的所属类型指向参数ktype(kobj->ktype = ktype)
2、对引用计数的操作
操作kobject的一个重要的函数是为包含它的对象设置引用计数,只要对象的引用计数不为0,那么对象就不能被释放。底层控制kobject引用计数的函数有:
struct kobject *kobject_get(struct kobject *kobj);
void kobject_put(struct kobject *kobj);
对kobject_get的成功调用将增加kobject的引用计数(+1),并返回指向kobject的指针。当不需要使用kobject的时候,调用
kobject_put来减少其引用计数(-1),并且当引用计数为0时,kobject_put还会调用kobject_cleanup来释放该对象。另外,注意
kobject_init初始化时设置kobject的引用计数为1。
3、kobject的添加、删除、移动
内核提供三个函数用于将一个kobject添加到kobject层次中去:
int kobject_add(struct kobject *kobj, struct kobject *parent,const char *fmt, ...);
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...);
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent);
在正确地初始化kobject之后(即成功调用kobject_init之后),就可以调用函数kobject_add将kobject加入到kobject层次中去,参数的意思也很明显,这里就不浪费时间说明参数了。
如果参数parent != NULL,那么kobj->parent = parent;如果parent == NULL,那么kobj->parent将被设置为该kobj所属的kset的kobject,如果该kobj还未确定所属kset,则该kobj将被置于sysfs树的根部。还是看一下下面的代码片段,以加深理解:
...
kobj->parent = parent;//设置kobj的parent为参数parent
...
struct kobject *parent;
parent = kobject_get(kobj->parent);//增加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加入到其所属kset的list链表中
kobj->parent = parent;
}
...
kobject_create_and_add动态创建了一个kobject结构体,将其初始化,将其加入到kobject层次中,并最终返回所创建的kobject的指针,当然如果函数执行失败,则返回NULL;而在调用kobject_init_and_add之前,kobject结构体必须已经创建好,动态创建或者静态声明均可。这两个函数可以理解为kobject_init和kobject_add的包裹函数。
内核还提供一个kobject_del函数用于将一个kobject从kobject层次中移除,即从sysfs中移除:
void kobject_del(struct kobject *kobj);
该函数将kobject从sysfs中移除,设置kobj->state_in_sysfs为0(表明kobj不在sysfs中),调用kobj_kset_leave将kobj与相应的kset脱离关联(即将kobj从其对应的kset的list链表中删除),对kobj的parent调用kobject_put以减少对其parent的引用计数,最后将
kobj的parent置为NULL。
内核提供kobject_move函数用于将一个kobject从一个层次移动到另一个层次:
int kobject_move(struct kobject *kobj, struct kobject *new_parent);
参数意义也很明显,需要注意的是,new_parent可以为NULL。关于该函数,这里暂不详细看了。
二、kobj_type
kobject是隐藏在sysfs虚拟文件系统背后的机制,sysfs中的每个目录,都对应着一个kobject,每个kobject都输出若干属性,这些属性在sysfs中表现为文件,是sysfs树的叶子节点。那么如何理解kobject和sysfs之间的关系?
1、sysfs中的一个目录对应一个kobject。当成功调用kobject_add时,将在sysfs中创建一个目录。通常这个目录下包含若干属性,即包含若干文件;
2、分配给kobject的名字(kobject->name)就是sysfs中目录的名字;
3、LDD3 P369 这条楞没看懂啥意思......
说了这么多,也该看看kobj_type的定义了:
struct kobj_type {
void (*release)(struct kobject *kobj);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
当kobject的引用计数为0时,就会释放该kobject的资源。那么,如何释放、调用哪个函数进行释放?答案就是调用 kobj_type结构的release成员。因此,每个kobject都必须有一个release方法,并且需要将其保存在kobj_type的release成员中。
另外,当创建kobject的时候,都会给每个kobject一系列默认属性。这些属性保存在kobj_type结构中。
default_attrs指向该类型kobject的默认属性列表,先看下attribute的定义:
struct attribute {
char * name;
struct module * owner;
mode_t mode;
};
attribute,即属性,它以文件的形式输出到sysfs的目录当中,在kobject对应的目录下面,是sysfs目录树的叶子节点,文件
名就是name。
default_attrs数组说明了都有哪些属性,但是没有告诉sysfs如何实现这些属性,这个任务交给了sysfs_ops成员,它指向的结构定义如下:
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};
sysfs_ops指针指向sysfs操作表,sysfs操作表包括两个函数store()和show()。当用户态读取属性时,show()函数被调用,该函数编码指定属性值存入buffer中返回给用户态,而store()函数用于存储用户态传入的属性值。
下面举个kobject.c中的例子来说明kobject和kobj_type如何关联起来的。从kobject_init函数的参数就可以看出,对一个kobject进行初始化时,需要提供此kobject所属的类型,即提供kobj_type结构,因此首先要准备kobj_type结构,说白了就是要准备release函数、sysfs_ops结构等。
/* default kobject attribute operations */
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct kobj_attribute *kattr;
ssize_t ret = -EIO;
kattr = container_of(attr, struct kobj_attribute, attr);
if (kattr->show)
ret = kattr->show(kobj, kattr, buf);
return ret;
}
static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct kobj_attribute *kattr;
ssize_t ret = -EIO;
kattr = container_of(attr, struct kobj_attribute, attr);
if (kattr->store)
ret = kattr->store(kobj, kattr, buf, count);
return ret;
}
struct sysfs_ops kobj_sysfs_ops = {
.show = kobj_attr_show,
.store = kobj_attr_store,
};
static void dynamic_kobj_release(struct kobject *kobj)
{
pr_debug("kobject: (%p): %s/n", kobj, __func__);
kfree(kobj);
}
static struct kobj_type dynamic_kobj_ktype = {
.release = dynamic_kobj_release,
.sysfs_ops = &kobj_sysfs_ops,
};
struct kobject *kobject_create(void)
{
struct kobject *kobj;
kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
if (!kobj)
return NULL;
kobject_init(kobj, &dynamic_kobj_ktype);//将kobj与kobj_type关联起来
return kobj;
}
从上述代码中可以看出,如果是动态创建的kobject,那么该kobject的类型就指向dynamic_kobj_ktype。对该结构的理解还有待加深,以后再慢慢看吧......
三、kset
kset是一组kobject的集合,其内的每个kobject都可以有不同的类型,即有不同的kobj_type,但是一个kset内的所有kobject总是希望以同样的方式被操作。看下kset的定义:
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
struct kset_uevent_ops *uevent_ops;
};
@list:kset包含的kobject的链表
@list_lock:操作list所需的锁
@kobj:一个kset其实也是一个kobject
@uevent_ops:该kset对应的热插拔操作集合。对于热插拔相关的,此处不多介绍,因为无碍主要结构的把握,并且我也不是很懂......
先看一下kset和kobject的关系,如下图:
kset总是在sysfs中出现,一旦设置了kset并把它添加到系统中,将在sysfs中创建一个目录。kset中的每一个kobject成员(kset->list链表上的成员)都将在sysfs中得到表述。
kset拥有与kobject类似的初始化和设置接口,如下:
void kset_init(struct kset *k);
int kset_register(struct kset *k);
void kset_unregister(struct kset *k);
struct kset *kset_create_and_add(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj);
未完,待续......
参考:
【1】http://blog.youkuaiyun.com/fudan_abc/archive/2007/09/01/1768296.aspx
【2】http://www.lupaworld.com/bbs/thread-38850-1-5.html
【3】LDD3 第十四章