LINUX设备驱动之设备模型一--kobject

    技术2025-10-06  8

    LINUX设备驱动之设备模型一kobject

    Eric Fang  2010-01-11

    --------------------------------------------------------------

    本站分析linux内核源码,版本号为2.6.32.3

    转载请注明出处:http://ericfang.cublog.cn/

    --------------------------------------------------------------

     

    LINUX设备驱动驱动程序模型的核心数据结构是kobjectkobject数据结构在/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都有它的父节点parentksetkobj_type指针,这三者是驱动模型的基本结构,ksetkobject的集合,在/linux/kobject.h中定义:

    struct kset {

           struct list_head list;

           spinlock_t list_lock;

           struct kobject kobj;

           struct kset_uevent_ops *uevent_ops;

    };

    可以看到每个kset内嵌了一个kobjectkobj字段),用来表示其自身节点,其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属性(在后面的分析中我们将会看到)。

    从上面的分析我们可以想象到kobjectksetkobj_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指向的内容赋给kobjectname字段。下面我们详细看看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),如果kobjname字段指向的内容为空,则为分配一个内存空间并用fmt指向的内容初始化,把地址赋给kobjname字段。

    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_getkobject_put分别增加和减少计数器,如果计数器为0,则释放该kobjectkobject_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,则会去检查kobjkset是否存在,如果存在就会把kset所嵌套的kobj作为其父节点,并把kobj添加到kset中去。

                  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>");

    打印一些调试信息,接着为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;

    }

    如果创建不成功,则回滚上面的操作,成功的话则设置kobjstate_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->parentNULL,就把&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;

    }

    得到kobjkobj_type,历遍kobj_typedefault_attrs并创建属性文件,文件的操作会回溯到sysfs_opsshowstore会调用封装了attributekobj_attribute结构的storeshow方法(在后面的代码中将会分析)。

    由于上面kobject_init(kobj, &dynamic_kobj_ktype)用默认dynamic_kobj_ktype作为kobj_type参数,而dynamic_kobj_ktypedefault_attrsNULL,所以这里没有创建属性文件。

    至此,我们已经知道了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目录及其目录下的属性文件,清kobjstate_in_sysfs标志,把kobjkset中删除,减少kobj->parent的计数并设其指针为空。 

    最新回复(0)