内核对象和集合
Linux驱动模型的基础是内核对象。它将总线类型、设备、驱动等看作是内核对象。内核对象的结构为kobject,相当于其它对象的基类。结构kobject的定义如下:
struct kobject {
const char *name; /*kobject的名字*/
struct list_head entry; /*将kobject链接到kset的连接件*/
struct kobject *parent; /*指向kobject的父对象的指针*/
struct kset *kset; /*如果kobject已链接到kset,则指向它*/
struct kobj_type *ktype; /*kobject的类型*/
struct sysfs_dirent *sd; /*指向kobject在sysfs内部树中的节点*/
struct kref kref; /*kobject的引用计数*/
unsigned int state_initialized:1; /*如果为1,表示kobject已被经过初始化*/
unsigned int state_in_sysfs:1; /*如果为1,表示kobject已被添加到内核关系树中*/
unsigned int state_add_uevent_sent:1; /*如果为1,表示kobject已发送过添加事件到用户空间*/
unsigned int state_remove_uevent_sent:1; /*如果为1,表示kobject已发送过删除事件到用户空间*/
unsigned int uevent_suppress:1; /*如果为1,表示抑制发送事件到用户空间*/
};
说明:本文所有的源代码均取自内核2.6.32。
对于内核对象kobject,有些成员和方法是内核对象类型特定的。即对该类型的所有内核对象,这些成员和方法是相同的。
struct kobj_type {
void (*release)(struct kobject *kobj); /*该类型的内核对象的释放方法*/
struct sysfs_ops *sysfs_ops; /*指向操作表结构的指针,其中实现该类型内核对象的属性读/写方法*/
struct attribute **default_attrs; /*该类型的内核对象的默认属性*/
};
kset表示内核对象的集合。结构kset的定义如下:
struct kset {
struct list_head list; /*该kset中的所有kobjetc的链表*/
spinlock_t list_lock; /*用于遍历该kset的所有kobject的自旋锁*/
struct kobject kobj; /*该kset的内嵌kobject(递归)*/
struct kset_uevent_ops *uevent_ops; /*该kset的uevent操作集。相应的函数在kobject发生某种事件时调用,kset可以添加新的环境变量,或者过滤一些uevent*/
};
创建或初始化内核对象
在使用内核对象之前,必须创建或初始化kobject结构体。对应的函数分别是kobject_create或kobject_init。如果用户已为kobject分配了空间,则只需要调用kobject_init。否则,需要调用kobject_create,该函数先为kobject分配空间,接着调用kobject_init。
函数kobject_create定义如下:
struct kobject *kobject_create(void)
{
struct kobject *kobj;
kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
if (!kobj)
return NULL;
kobject_init(kobj, &dynamic_kobj_ktype);
return kobj;
}
函数kobject_init定义如下:
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
char *err_str;
if (!kobj) {
err_str = "invalid kobject pointer!";
goto error;
}
if (!ktype) {
err_str = "must have a ktype to be initialized properly!\n";
goto error;
}
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();
}
函数首先进行了一些必要的检查。内核对象原则上不应该被重复初始化,但是如果出现这种情况,程序也不退出,而是打印错误信息后继续执行。然后调用函数kobject_init_internal初始化内核对象中的部分变量。
函数kobject_init_internal定义如下:
static void 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;
}
kobject_init_internal初始化了内核对象中的部分变量,主要包括:
- 初始化内核对象的内嵌引用计数;
- 初始化内核对象用于链接到kset的连接件;
- 当前内核对象还没被添加到sysfs文件系统,此外,也没有向用户空间发送任何uevent事件,因此对象的变量都设为0;
- 该函数执行结束时,意味着内核对象已初始化完成,设置state_initialized为1。
将内核对像添加到sysfs文件系统
在调用kobject_init后,要向sysfs注册该kobject,所以必须调用kobject_add函数。如果kobject结构体不准备在sysfs层次使用,就不用调用kobject_add函数。
函数kobject_add定义如下:
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...)
{
va_list args;
int retval;
if (!kobj)
return -EINVAL;
if (!kobj->state_initialized) {
printk(KERN_ERR "kobject '%s' (%p): tried to add an "
"uninitialized object, something is seriously wrong.\n", kobject_name(kobj), kobj);
dump_stack();
return -EINVAL;
}
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);
return retval;
}
函数kobject_add_varg定义:
static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs)
{
int retval;
retval = kobject_set_name_vargs(kobj, fmt, vargs);
if (retval) {
printk(KERN_ERR "kobject: can not set name properly!\n");
return retval;
}
kobj->parent = parent;
return kobject_add_internal(kobj);
}
调用kobject_set_name_vargs函数设置内核对象kobject的名字,并设置了父对象。
函数kobject_add_internal定义:
static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
struct kobject *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;
return error;
}
如果设置了内核对象的kset变量,并且内核对象的parent变量为空,则将它重新设置为kset变量的内嵌内核对象。内核对象的parent变量将决定内核对象在sysfs树中的位置。因此,在该函数调用之前,存在以下三种可能:
- 如果内核对象的parent和kset变量都已设置,则内核对象在sysfs树中的位置由其parent决定,它将被放置在内核对象的父对象所对应的目录;
- 如果内核对象的parent为NULL,而kset已设置,在此会把kset变量的内嵌内核对象值赋值给内核对象的parent变量,因此kset决定了内核对象在sysfs树中的位置,它将放置在kset的内嵌内核对象所对应的目录下;
- 如果内核对象的parent和kset均为NULL,则内核对象将被放置在sysfs文件系统树的根目录下。