Linux内核之设备驱动-底层数据结构kobject/kset
kobject
kobject是组成device、driver、bus、class的基本结构。如果把前者看成基类,则后者均为它的派生产物。device、driver、bus、class构成了设备模型,而kobject内嵌于其中,将这些设备模型的部件组织起来,并形成了sysfs文件系统。kobject就是device、driver、bus、class在文件系统中的代表。在sysfs操作设备时,也必须通过kobject这个中间人来完成。
kobject的主要功能如下:
对象的引用计数
通常一个内核对象被创建时,不可能知道该对象存活的时间。跟踪此对象生命周期的一个方法是使用引用计数。当内核中没有代码持有该对象的引用时,该对象将结束自己的有效生命周期,并且可以被删除。
sysfs表述
在sysfs中显示的每一个对象,都对应一个kobject,它被用来与内核交互并创建它的可见表述。
数据结构关联
从整体上看,设备模型是一个友好而复杂的数据结构,通过在其间的大量连接而构成一个多层次的体系结构。Kobject实现了该结构并把它们聚合在一起。
uevent事件处理
当系统中的硬件被热插拔时,在kobject子系统控制下,将产生事件以通知用户空间。
下面以2.6.29版本(本文涉及代码均为此版本)内核源码一一介绍kobject的功能。在kernel/include/linux/kobject.h中,kobject结构定义如下:
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;
};
*name
kobject的名字,每个kobject都对应着sysfs下的一个文件夹,该名字也是对应的文件夹的名字。
entry
双向链表指针,用于将同一kset集合中的kobject链接到一起,便于访问。
*parent
kobject对应的父kobject节点,在sysfs表现为上一级目录。
*kset
kobject所在的集合的指针,kset概念将在kset一节中描述。
*ktype
kobject对象类型指针,随后将会介绍。
*sd
sd用于表示VFS文件系统的目录项,由此可见它是设备与文件之间的桥梁。在sysfs节会对此结构进行分析。
kref
对象引用计数器。引用计数器的作用前面已经讲过。
state_initialized
初始化标志位,在对象初始化时被置位。
state_in_sysfs
kobject对象在sysfs中的状态,创建则置1,否则为0。亦即kobject对应的目录在sysfs中是否被创建。
state_add_uevent_sent
添加设备的uevent事件是否发送标志,添加设备时会向用户空间发送uevent事件,请求新增设备。
state_remove_uevent_sent
删除设备的uevent事件是否发送标志,删除设备时会向用户空间发送uevent事件,请求卸载设备。
kset是嵌入相同类型结构的kobject集合。我们可以认为它是kobject的顶层容器类。kset也是基于sysfs的,维系着设备、驱动等等分类与链接关系。图1-1(来自LDD3,但稍作修改)可清晰表示kset与kobject的关系。
图1-1:kset与kobject关系
下面我们来看一下kset的结构:
struct kset
{
struct
list_head list;
spinlock_t
list_lock;
struct
kobject kobj;
struct
kset_uevent_ops *uevent_ops;
};
从结构体中我们可以看到kset与kobject最大的不同就是多了kset_uevent_ops类型的成员。因此,我们可理解kset就是为了让一组kobject使用相同的uevent处理函数。
uevent知识请参考uevent和udev一章。
前面说过kobject对应sysfs中的文件夹,但作为一个文件系统,不可能没有文件。下面将要讲到的属性即对应这里的文件。我们先来看kobject中的一个重要成员*ktype,类型如下:
struct kobj_type
{
void
(*release)(struct kobject *kobj);
struct
sysfs_ops *sysfs_ops;
struct
attribute **default_attrs;
};
*release
意如其名,即当kobject引用计数器为0时,用来释放kobject对象。
*sysfs_ops
根据default_attrs中的mode要求,提供方法操作指定属性文件。一般只有读写两个函数,如下:
struct sysfs_ops
{
ssize_t (*show)(struct
kobject *, struct attribute *,char *);
ssize_t (*store)(struct
kobject *,struct attribute *,const char *,
size_t);
};
**default_attrs
保存了属性列表,用于创建该类型的每一个kobject文件。结构如下:
struct attribute
{
const
char *name;
struct
module *owner;
mode_t mode;
};
*name
属性名,对应于kobject的sysfs目录中的一个文件。
*owner
指向模块的指针,该模块负责实现这些属性。源码注释已明确指出该字段已不在使用,目前存在的原因就是为了保持向上兼容。
mode
指明该属性文件是只读只写还是可读可写,谁可写等等。
除了在初始化时指定属性外,我们还可以根据需要使用函数对属性进行增删,如下:
int
sysfs_create_file(struct kobject * kobj, const struct attribute *
attr);
void
sysfs_remove_file(struct kobject * kobj, const struct attribute *
attr);
请注意,属性可以任意增删,但方法sysfs_ops确是唯一的不可改变,所以必须确保该方法可以处理新的属性。