怎么了选择
分类: LINUX
2012-01-13 11:20:08
LINUX 设备驱动驱动程序模型的核心数据结构是 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;
};
每个 kobject 都有它的父节点 parent 、 kset 、 kobj_type 指针,这三者是驱动模型的基本结构, kset 是 kobject 的集合,在 /linux/kobject.h 中定义:
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
struct kset_uevent_ops *uevent_ops;
};
可以看到每个 kset 内嵌了一个 kobject ( kobj 字段),用来表示其自身节点,其 list 字段指向了所包含的 kobject的链表头。我们在后面的分析中将看到 kobject 如果没有指定父节点, parent 将指向其 kset 内嵌的 kobject 。
每个 kobject 都有它的 kobj_type 字段指针,用来表示 kobject 在文件系统中的操作方法, kobj_type 结构也在/linux/kobject.h 中定义:
struct kobj_type {
void (*release)(struct kobject *kobj);
struct sysfs_ops *sysfs_ops;
struct attribute ** default_attrs;
};
release 方法是在 kobject 释放是调用, sysfs_ops 指向 kobject 对应的文件操作, default_attrskobject 的默认属性,sysfs_ops 的将使用 default_attrs 属性(在后面的分析中我们将会看到)。
从上面的分析我们可以想象到 kobject 、 kset 、 kobj_type 的层次结构:
我们可以把一个 kobject 添加到文件系统中去(实际上是添加到其父节点所代表的 kset 中去),内核提供kobject_create_and_add() 接口函数:
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
struct kobject *kobj;
int retval;
kobj = kobject_create();
if (!kobj)
return NULL;
retval = kobject_add(kobj, parent, "%s", name);
if (retval) {
printk(KERN_WARNING "%s: kobject_add error: %d/n",
__func__, retval);
kobject_put(kobj);
kobj = NULL;
}
return kobj;
}
kobject _create() 为要创建的 kobject 分配内存空间并对其初始化。
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() 对 kobject 基本字段进行初始化,用输入参数设置 kobj_type 属性。
这里粘出代码以供参考:
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();
}
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_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;
}
在上面的初始化中已把位变量设位 1
va_start(args, fmt) 和 va_end(args) 使用可变参数(可见参数用法不在这里分析),在 kobject_add_varg 中将把 fmt指向的内容赋给 kobject 的 name 字段。下面我们详细看看 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(kobj, fmt, vargs) ,如果 kobj 的 name 字段指向的内容为空,则为分配一个内存空间并用fmt 指向的内容初始化,把地址赋给 kobj 的 name 字段。
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
va_list vargs)
{
const char *old_name = kobj->name;
char *s;
if (kobj->name && !fmt)
return 0;
kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
if (!kobj->name)
return -ENOMEM;
/* e some of these buggers have '/' in the name ... */
while ((s = strchr(kobj->name, '/')))
s[0] = '!';
kfree(old_name);
return 0;
}
char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
{
unsigned int len;
char *p;
va_list aq;
va_copy(aq, ap);
len = vsnprintf(NULL, 0, fmt, aq);
va_end(aq);
p = kmalloc(len+1, gfp);
if (!p)
return NULL;
vsnprintf(p, len+1, fmt, ap);
return p;
}
继续 kobject_add_varg ()返回 kobject_add_internal(kobj) ,就是在这个函数理为 kobj 创建文件系统结构:
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;
}
检查 kobj 和它的 name 字段,不存在则返回错误信息。
parent = kobject_get(kobj->parent);
获得其父节点,并增加父节点的计数器, kobject 结构中的 kref 字段用于容器的计数, kobject_get 和 kobject_put分别增加和减少计数器,如果计数器为 0 ,则释放该 kobject , kobject_get 返回该 kobject 。
/* 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;
}
在这里我们可以看到,如果调用 kobject_create_and_add ()时参数 parent 设为 NULL ,则会去检查 kobj 的 kset是否存在,如果存在就会把 kset 所嵌套的 kobj 作为其父节点,并把 kobj 添加到 kset 中去。
pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'/n",
kobject_name(kobj), kobj, __func__,
parent ? kobject_name(parent) : "
kobj->kset ? kobject_name(&kobj->kset->kobj) : "
打印一些调试信息,接着为 kobj 创建目录:
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;
}
如果创建不成功,则回滚上面的操作,成功的话则设置 kobj 的 state_in_sysfs 标志。
在看看 create_dir ()函数中具体创建了那些内容:
static int create_dir(struct kobject *kobj)
{
int error = 0;
if (kobject_name(kobj)) {
error = sysfs_create_dir(kobj);
if (!error) {
error = populate_dir(kobj);
if (error)
sysfs_remove_dir(kobj);
}
}
return error;
}
sysfs_create_dir ()先为 kobj 创建了一个目录文件
int sysfs_create_dir(struct kobject * kobj)
{
struct sysfs_dirent *parent_sd, *sd;
int error = 0;
BUG_ON(!kobj);
if (kobj->parent)
parent_sd = kobj->parent->sd;
else
parent_sd = &sysfs_root;
error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);
if (!error)
kobj->sd = sd;
return error;
}
如果 kobj->parent 为 NULL ,就把 &sysfs_root 作为父节点 sd ,即 在/sys 下面创建结点。
然后调用 populate_dir :
static int populate_dir(struct kobject *kobj)
{
struct kobj_type *t = get_ktype(kobj);
struct attribute *attr;
int error = 0;
int i;
if (t && t->default_attrs) {
for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
error = sysfs_create_file(kobj, attr);
if (error)
break;
}
}
return error;
}
得到 kobj 的 kobj_type ,历遍 kobj_type 的 default_attrs 并创建属性文件,文件的操作会回溯到sysfs_ops的show 和store 会调用封装了attribute 的kobj_attribute 结构的store 和show 方法(在后面的代码中将会分析)。
由于上面kobject_init(kobj, &dynamic_kobj_ktype) 用默认dynamic_kobj_ktype 作为 kobj_type 参数,而dynamic_kobj_ktype 的 default_attrs 为 NULL ,所以这里没有创建属性文件。
至此,我们已经知道了 kobject_create_and_add() 函数创建 kobject ,挂到父 kobject ,并设置其 kobj_type ,在文件系统中为其创建目录和属性文件等。
另外,如果我们已静态定义了要创建的 kobject ,则可以调用 kobject_init_and_add() 来注册 kobject ,其函数如下:
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...)
{
va_list args;
int retval;
kobject_init(kobj, ktype);
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);
return retval;
}
通过上面的分析我们很轻松就能理解这个函数。
内核提供注销 kobject 的函数是 kobject_del()
void kobject_del(struct kobject *kobj)
{
if (!kobj)
return;
sysfs_remove_dir(kobj);
kobj->state_in_sysfs = 0;
kobj_kset_leave(kobj);
kobject_put(kobj->parent);
kobj->parent = NULL;
}
删除 kobj 目录及其目录下的属性文件,清 kobj 的 state_in_sysfs 标志,把 kobj 从 kset 中删除,减少 kobj->parent 的计数并设其指针为空。