Chinaunix首页 | 论坛 | 博客
  • 博客访问: 803947
  • 博文数量: 869
  • 博客积分: 201
  • 博客等级: 入伍新兵
  • 技术积分: 3376
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-21 19:42
文章分类

全部博文(869)

文章存档

2014年(4)

2013年(415)

2012年(453)

我的朋友

分类:

2012-10-19 13:31:06

 

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;

 

       /* ewww... 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_opsshow 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 的计数并设其指针为空。

阅读(283) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~