2.6内核引入了一个新特性--统一的设备模型.其优点如下:
.代码重复最小化;
.提供诸如引用计数这样的统一机制;
.可以列举系统中所有的设备,观察它们的状态,并且查看它们连接的总线;
.将系统中全部设备结构以树的形式完整、有效地展现出来,包括所有的总线和内部连接;
.可以将设备和其对应的驱动联系起来,反之亦然;
.可以将设备按照类型加以归类,如分类输入设备,而无需要理解设备的拓扑结构;
.可以沿设备树的叶子向其根方向依次遍历,保证能以正确的顺序关闭各设备的电源.
最后一点是实现设备模型的最初动机.若想在内核中实现智能电源管理,就需要建立表示系统中设备拓扑关系的树结构.当在树上端的设备关闭电源时,内核必须首先关闭该设备节点以下的(处于叶子上)设备电源.比如内核需要先关闭一个USB鼠标,然后才可关闭USB控制器;同样内核也必须在关闭PCI总线前先关闭USB控制器.简而言之,若要准确而又高效地完成上述电源管理,内核无疑需要一颗设备树.
17.1 kobject
设备模型的核心是kobject,它由struct kobject结构体表示.kobject对象最形象的意义是对应sys目录下的一个目录.
struct kobject {
char * k_name;
char name[KOBJ_NAME_LEN];
struct kref kref;
struct list_head entry;
struct kobject * parent;
struct kset * kset;
struct kobj_type * ktype;
struct dentry * dentry;
};
各域的意义如下:
K_name:指向kobject名称的起始位置.如果名称长度小于KOBJ_NAME_LEN,该kobject的名称存放到name数组中,k_name指向数组头;如果名称长度大于KOBJ_NAME_LEN,则动态分配一个足够大的缓冲区存放kobject的名称,这时k_name指向分配的缓存区;
Parent:同样是struct kobject类型.指向kobject的父对象,用来实现sysfs的层次结构;
Dentry:指向dentry结构体,在sysfs中该结构体就是表示这个kobject;
Kref、ktype、和kset这些字段指向了kobject所需要的其他结构体;
Kobject单独存在的意义不大,一般嵌入到其他的结构体中,如struct cdev.如下:
struct cdev {
struct kobject kobj;
struct module *owner;
struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
Kobject对应sysfs下的一个目录,这个目录下面可以包含文件,而这些文件可以包含一些设备及其驱动的一些信息.这些文件及文件的操作集由kobject结构体中的kobj_type指向.如下:
struct kobj_type {
void (*release)(struct kobject *);
struct sysfs_ops * sysfs_ops;
struct attribute ** default_attrs;
};
其中,default_attrs便是文件;
Sysfs_ops便是对应文件的操作集.
下面是对kobj_type的解说.
17.2 ktype
上面说了,kobject是目录,而我们一般的信息都存放在文件里面,而这些信息的读写操作也必须有相应的方法.因此,也必须得包含在其中.
struct kobj_type {
void (*release)(struct kobject *);
struct sysfs_ops * sysfs_ops;
struct attribute ** default_attrs;
};
各域意义如下:
Release:在kobject引脚计数减至0时被调用的析构函数.
Sysfs_ops:当前kobject目录下文件的操作方法集合;
Default_attrs:是一个数组,其中的每个元素表示一个文件.因此,一个目录下面可以有多个文件便是依此来实现的.数组中最后一项必须为NULL.
17.3 kset
Kset可以现在成一个容器,里面存放相关的kobject对象.换句话来说,kset在sysfs的最表象的意义是可包含目录的目录,当然,它还可以包含文件(里面的ktype域).实现思想源码如下:
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...)
{
... ...;
retval = kobject_add_varg(kobj, parent, fmt, args);
... ...;
}
static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
const char *fmt, va_list vargs)
{
... ...;
return kobject_add_internal(kobj);
}
static int kobject_add_internal(struct kobject *kobj)
{
... ...;
if (kobj->kset) {
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);
kobj->parent = parent;
}
... ...;
}
Kset用结构体struct kset表示.定义于<linux/kobject.h>中.
struct kset {
struct subsystem * subsys;
struct kobj_type * ktype;
struct list_head list;
struct kobject kobj;
struct kset_hotplug_ops * hotplug_ops;
};
各域意义如下:
Subsys:指向该结构体相关的struct subsystem,下面讲述;
Ktype:用于指向本kset中kobject对象中的文件的集合;
List:连接该集合(kset)中所有的kobject对象;
Kobj:指向本kset下的kobject对象;
Hotplug_ops:指向一个用于处理集合中kobject对象的热插拔操作的结构体.
17.4 subsystem
Subsystem的意义是一个或多个kset的大集合.其实,它只比kset多了一个信号灯量.在后期的LK2.6内核,便把subsystem去掉了.如下:
struct subsystem {
struct kset kset;
struct rw_semaphore rwsem;
};
17.5 别混淆了这些结构体
Ktype、kobject、kset和subsystem四者的关系如下图所示:
17.6 管理和操作kobject
在实际的驱动编程中,我们并不需要直接处理kobject.这里了解下管理和操作它的外部接口就可以了.
初始化kobject对象的示意代码如下:
Kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
If(!kobj)
Return -ENOMEM;
Memset(kobj,0,sizeof(*kobj));
Kobj->kset = kset;
Kobj->parent = parent_kobj;
Kobject_init(struct kobject *kobj);
初始完后,用kobject_set_name()函数为该kobject设置名称:
Int kobject_set_name(struct kobject *kobj,const char *fmt,...);
17.7 引用计数
Kobject主要功能之一就是为我们提供了一个统一的引用计数系统.初始化后,kobject的引用计数为1.只要其计数不为0,该对象继续保留在内存中.当该对象的引用计数为0,对象可以被销毁.任何包含对象引用的代码首先要增加该对象的引用计数,当代码结束后减少它的引用计数.
增加引用计数:
Struct kobject *kobject_get(struct kobject *kobj);
减少引用计数:
Void kobject_put(struct kobject *kobj);
如下代码:
void kobject_init(struct kobject * kobj)
{
kref_init(&kobj->kref);
INIT_LIST_HEAD(&kobj->entry);
kobj->kset = kset_get(kobj->kset);
}
void kobject_put(struct kobject * kobj)
{
if (kobj)
kref_put(&kobj->kref, kobject_release);
}
17.8 sysfs
Sysfs文件系统是基于RAM的虚拟文件系统.它为我们提供了kobject对象层次的结构视图.帮助用户能以一个简单的文件系统方式来观察系统中各设备的拓朴结构.
Sysfs的把kobject对象与目录项(directory entry)紧密联系起来,这点通过kobject对象中的dentry字段实现.下面是挂载于/sys目录下的sysfs文件系统的局部视图: