免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1621 | 回复: 0
打印 上一主题 下一主题

LINUX设备驱动之设备模型一--kobject [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-11 15:06 |只看该作者 |倒序浏览


Author: Eric Fang  
Date:     2010-01-11
-----------------------------------------------------------------
本站分析linux内核源码,版本号为2.6.32.3
转载请注明出处:
http://ericfang.cublog.cn/
-----------------------------------------------------------------
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_init_and_add接口函数:
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_init设置kobject的kobj_type属性,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_init_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) != 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_and_add()函数将子kobject挂到父kobject,并设置其kobj_type,在文件系统中为其创建目录和属性文件等。

内核提供注销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的计数并设其指针为空。


接着分析kset的创建,我们已经知道了kset内嵌了kobject来表示自身的节点,创建kset就要完成其内嵌kobject,注册kset时会产生一个事件,事件而最终会调用uevent_ops字段指向结构中的函数,这个事件是通过用户空间的hotplug程序处理。下面我们一步一步分析。
内核同样提供了创建和注册kset的函数kset_create_and_add()
struct kset *kset_create_and_add(const char *name,
                 struct kset_uevent_ops *uevent_ops,
                 struct kobject *parent_kobj)
{
    struct kset *kset;
    int error;

    kset = kset_create (name, uevent_ops, parent_kobj);
    if (!kset)
<SPAN lang=EN-US style="FONT-SIZE: 10pt;

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/92745/showart_2145668.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP