Chinaunix首页 | 论坛 | 博客
  • 博客访问: 54869
  • 博文数量: 21
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 60
  • 用 户 组: 普通用户
  • 注册时间: 2013-04-23 15:40
文章分类

全部博文(21)

文章存档

2013年(21)

我的朋友

分类: LINUX

2013-05-24 09:04:01

原文地址:Kobject模型 作者:gc5084

一。Kobject
每个在内核中注册的kobject都对应于sysfs文件系统中的一个目录。
kobject在文件include/linux/kobject中定义

点击(此处)折叠或打开

  1. struct kobject {
  2.     const char        *name;                    //kobject的名称
  3.     struct list_head    entry;            //kobject结构链表
  4.     struct kobject        *parent;        //父kobject结构体
  5.     struct kset        *kset;                    //kset集合
  6.     struct kobj_type    *ktype;            //kobject的类型描述符
  7.     struct sysfs_dirent    *sd;            //sysfs文件目录
  8.     struct kref        kref;                        //kobject引用计数
  9.     unsigned int state_initialized:1;    //kobject是否初始化
  10.     unsigned int state_in_sysfs:1;    //是否已经加入sysfs
  11.     unsigned int state_add_uevent_sent:1;
  12.     unsigned int state_remove_uevent_sent:1;
  13.     unsigned int uevent_suppress:1;
  14. };
sysfs组织结构,进入sysfs目录中。有block bus class dev devices firmware fs kernel module power这些目录。具体代表看名字差不多就可以看出。在层次结构上,假如有一个设备A。将有一个名称为A的目录。A设备是在B总线上。那A设备应该在bus目录下的B总线下。A设备肯定会有设备的属性(ktype),例如是音频设备则应该有音量属性,则音量属性将在A设备目录下有个音量属性文件。在使用设备时,如果要改变音量大小,则可以写属性文件入音量指。得到音量大小时,可以读取属性文件中的音量值。

 二。Kobject初始化
初始化一个kobject结构体变量,kobject_init函数(lib/kobject.c),调用此函数前应先将kobject变量成员全部置0

点击(此处)折叠或打开

  1. /**
  2.  * kobject_init - initialize a kobject structure
  3.  * @kobj: pointer to the kobject to initialize
  4.  * @ktype: pointer to the ktype for this kobject.
  5.  *
  6.  * This function will properly initialize a kobject such that it can then
  7.  * be passed to the kobject_add() call.
  8.  *
  9.  * After this function is called, the kobject MUST be cleaned up by a call
  10.  * to kobject_put(), not by a call to kfree directly to ensure that all of
  11.  * the memory is cleaned up properly.
  12.  */
  13. void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
  14. {
  15.     char *err_str;

  16.     if (!kobj) {    //检查kobj变量是否为空
  17.         err_str = "invalid kobject pointer!";
  18.         goto error;
  19.     }
  20.     if (!ktype) {    //检查ktype类型变量是否为空
  21.         err_str = "must have a ktype to be initialized properly!\n";
  22.         goto error;
  23.     }
  24.     if (kobj->state_initialized) {    //是否已经初始化过
  25.         /* do not error out as sometimes we can recover */
  26.         printk(KERN_ERR "kobject (%p): tried to init an initialized "
  27.          "object, something is seriously wrong.\n", kobj);
  28.         dump_stack();
  29.     }

  30.     kobject_init_internal(kobj);    //进一步初始化kobj内部成员
  31.     kobj->ktype = ktype;    //将参数中传来的ktype变量赋值给kobj的ktype变量。
  32.     return;

  33. error:
  34.     printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
  35.     dump_stack();
  36. }
分析kobject_init_internal函数(lib/kobject.c),此函数主要设置一些kobj中的一些变量

点击(此处)折叠或打开

  1. static void kobject_init_internal(struct kobject *kobj)
  2. {
  3.     if (!kobj)    //kobj是否为空
  4.         return;
  5.     kref_init(&kobj->kref);    //增加kobject的引用计数,kref_set(kref, 1);
  6.     INIT_LIST_HEAD(&kobj->entry);    //初始化kobj的链表
  7.     kobj->state_in_sysfs = 0;    //kobject还没有注册到sysfs中
  8.     kobj->state_add_uevent_sent = 0;    //
  9.     kobj->state_remove_uevent_sent = 0;
  10.     kobj->state_initialized = 1;
  11. }
三。kobj_type
对象的属性结构体kobj_type(include/linux/kobject.h)

点击(此处)折叠或打开

  1. struct kobj_type {
  2.     void (*release)(struct kobject *kobj);    //释放函数(驱动编写时提供),此函数会被kobject_put函数调用
  3.     struct sysfs_ops *sysfs_ops;    //属性文件的操作函数(只有读和写操作)
  4.     struct attribute **default_attrs;    //属性数组
  5. };
1.讨论kobj_type和kobject的关系,就要先说说kobject的引用。引用一个kobject使用函数kobject_get()这个函数会增加kobject的引用并返回kobject的指针。增加其引用是通过其kobject中断哦kref变量完成的。对kobject的引用管理主要是为了知道被引用的情况,如引用不为0就不能销毁kobject对象,引用为0时则调用相应的释放函数等。

点击(此处)折叠或打开

  1. struct kobject *kobject_get(struct kobject *kobj)
  2. {
  3.     if (kobj)
  4.         kref_get(&kobj->kref);
  5.     return kobj;
  6. }

点击(此处)折叠或打开

  1. void kref_get(struct kref *kref)
  2. {
  3.     WARN_ON(!atomic_read(&kref->refcount));
  4.     atomic_inc(&kref->refcount);    //将kref中的这个原子变量加1
  5.     smp_mb__after_atomic_inc();
  6. }
减少一个kobject对象的引用使用函数kobject_put()。当一个kobject对象的引用被减少到0时,程序就应该释放这个kobject相关的资源。所以在减少引用的函数中就应该有调用释放资源的相关代码,在下面内核代码中我们可以看到。

点击(此处)折叠或打开

  1. void kobject_put(struct kobject *kobj)
  2. {
  3.     if (kobj) {
  4.         if (!kobj->state_initialized)    //若kobj没有初始化就不能减少其引用
  5.             WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "
  6.              "initialized, yet kobject_put() is being "
  7.              "called.\n", kobject_name(kobj), kobj);
  8.         kref_put(&kobj->kref, kobject_release); //减少kref计数
  9.     }
  10. }
  11. int kref_put(struct kref *kref, void (*release)(struct kref *kref))
  12. {
  13.     WARN_ON(release == NULL);    //为空警告
  14.     WARN_ON(release == (void (*)(struct kref *))kfree); //如果release函数就是kfree,则警告(即release函数不能是简单的kfree)

  15.     if (atomic_dec_and_test(&kref->refcount)) {    //递减原子变量并检查其值
  16.         release(kref);    //回调release函数
  17.         return 1;
  18.     }
  19.     return 0;
  20. }
那这个release函数在哪里保存呢,这就和kobj_type结构有关系了。上面我们可以看到kobj_type中有一个release函数指针,就是保存在这里。每一个kobject的ktype都指向一个kobj_type,它保存了这个kobject类型的release函数指针。

四。Kset集合
1.Kset是具有相同类型的kobject集合。一个Kset集合可以表示在/sys/drivers/目录下,表示一类驱动程序。kobject则表示一个具体的驱动目录。这样kset则可以将kobject组织成层次化结构。

点击(此处)折叠或打开

  1. /**
  2.  * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
  3.  *
  4.  * A kset defines a group of kobjects. They can be individually
  5.  * different "types" but overall these kobjects all want to be grouped
  6.  * together and operated on in the same manner. ksets are used to
  7.  * define the attribute callbacks and other common events that happen to
  8.  * a kobject.
  9.  *
  10.  * @list: the list of all kobjects for this kset
  11.  * @list_lock: a lock for iterating over the kobjects
  12.  * @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
  13.  * @uevent_ops: the set of uevent operations for this kset. These are
  14.  * called whenever a kobject has something happen to it so that the kset
  15.  * can add new environment variables, or filter out the uevents if so
  16.  * desired.
  17.  */
  18. struct kset {
  19.     struct list_head list;    //这个链表存放这个kset关联的所有kobject
  20.     spinlock_t list_lock;        //维护此链表的锁
  21.     struct kobject kobj;        //内嵌的kobject。这样kset本身也是一个kobject也被表现为一个目录
  22.     struct kset_uevent_ops *uevent_ops;    //支持热插拔事件的函数集
  23. };
kset中的kobject对象,所有属于这个kset集合的kobject对象的parent指针,均指向这个内嵌的kobject,也即表示在一个kset集合中的kobject是相同类型的他们有相同的parent对象。kset的引用计数也就是内嵌的kobject的引用计数。
所以kobject和kset的关系简单来讲,就是
1.kset是kobject的一个顶层容器,它包含了相同类型的kobject,kset中有链表成员保存所有的kobject指向。
2.kobject中的kset指针指向了一个kset
3.kset中有kobject对象,表明了kset也可以有kobject相关的操作。
4.kset链表中的kobject对象的parent指针一般都指向kset内嵌的kobject对象。
kset和kobject关系基本如下图,

kset_uevent_ops热插拔事件
热插拔事件是用内核空间发送到用户空间的通知。表明内核中的某些配置已经发生变化。用户空间则会根据这些信息做相应的处理。例如,U盘插入USB系统时,会产生一个热插拔事件,内核会捕捉到这个热插拔事件,然后调用/sbin/hotplug程序,该程序通知加载驱动程序来相应U盘的插入动作。
热插拔函数集的定义在include/linux/koject.h中

点击(此处)折叠或打开

  1. struct kset_uevent_ops {
  2.     int (*filter)(struct kset *kset, struct kobject *kobj);        //事件过滤函数
  3.     const char *(*name)(struct kset *kset, struct kobject *kobj);    //事件名称函数
  4.     int (*uevent)(struct kset *kset, struct kobject *kobj,struct kobj_uevent_env *env);
  5.         //uevent函数可在热插拔程序执行前,向环境变量写值
  6. };
详细的热插拔事件先不在这里分析。

2.注册一个kset

点击(此处)折叠或打开

  1. /**
  2.  * kset_register - initialize and add a kset.
  3.  * @k: kset.
  4.  */
  5. int kset_register(struct kset *k)
  6. {
  7.     int err;

  8.     if (!k)
  9.         return -EINVAL;

  10.     kset_init(k);
  11.     err = kobject_add_internal(&k->kobj);    //将kset中的kobject添加进sysfs,函数将在后面讲解
  12.     if (err)
  13.         return err;
  14.     kobject_uevent(&k->kobj, KOBJ_ADD);
  15.     return 0;
  16. }
  17. /**
  18.  * kset_init - initialize a kset for use
  19.  * @k: kset
  20.  */
  21. void kset_init(struct kset *k)
  22. {
  23.     kobject_init_internal(&k->kobj);
  24.     INIT_LIST_HEAD(&k->list);
  25.     spin_lock_init(&k->list_lock);
  26. }
我们使用函数kset_create_and_add()还可以一次性将kset创建并注册进sysyfs

点击(此处)折叠或打开

  1. /**
  2.  * kset_create_and_add - create a struct kset dynamically and add it to sysfs
  3.  *
  4.  * @name: the name for the kset
  5.  * @uevent_ops: a struct kset_uevent_ops for the kset
  6.  * @parent_kobj: the parent kobject of this kset, if any.
  7.  *
  8.  * This function creates a kset structure dynamically and registers it
  9.  * with sysfs. When you are finished with this structure, call
  10.  * kset_unregister() and the structure will be dynamically freed when it
  11.  * is no longer being used.
  12.  *
  13.  * If the kset was not able to be created, NULL will be returned.
  14.  */
  15. struct kset *kset_create_and_add(const char *name,
  16.                  struct kset_uevent_ops *uevent_ops,
  17.                  struct kobject *parent_kobj)
  18. {
  19.     struct kset *kset;
  20.     int error;

  21.     kset = kset_create(name, uevent_ops, parent_kobj);    //根据参数创建一个kset
  22.     if (!kset)
  23.         return NULL;
  24.     error = kset_register(kset);                //将kset注册进sysfs,函数在上面已经分析过
  25.     if (error) {
  26.         kfree(kset);
  27.         return NULL;
  28.     }
  29.     return kset;
  30. }

点击(此处)折叠或打开

  1. /**
  2.  * kset_create - create a struct kset dynamically
  3.  *
  4.  * @name: the name for the kset
  5.  * @uevent_ops: a struct kset_uevent_ops for the kset
  6.  * @parent_kobj: the parent kobject of this kset, if any.
  7.  *
  8.  * This function creates a kset structure dynamically. This structure can
  9.  * then be registered with the system and show up in sysfs with a call to
  10.  * kset_register(). When you are finished with this structure, if
  11.  * kset_register() has been called, call kset_unregister() and the
  12.  * structure will be dynamically freed when it is no longer being used.
  13.  *
  14.  * If the kset was not able to be created, NULL will be returned.
  15.  */
  16. static struct kset *kset_create(const char *name,
  17.                 struct kset_uevent_ops *uevent_ops,
  18.                 struct kobject *parent_kobj)
  19. {
  20.     struct kset *kset;
  21.     int retval;

  22.     kset = kzalloc(sizeof(*kset), GFP_KERNEL);
  23.     if (!kset)
  24.         return NULL;
  25.     retval = kobject_set_name(&kset->kobj, name);    //设置kobject名称
  26.     if (retval) {
  27.         kfree(kset);
  28.         return NULL;
  29.     }
  30.     kset->uevent_ops = uevent_ops;
  31.     kset->kobj.parent = parent_kobj;            //设置kset的kobject的父对象

  32.     /*
  33.      * The kobject of this kset will have a type of kset_ktype and belong to
  34.      * no kset itself. That way we can properly free it when it is
  35.      * finished being used.
  36.      */
  37.     kset->kobj.ktype = &kset_ktype;    //设置kset的kobject的默认属性
  38.     kset->kobj.kset = NULL;

  39.     return kset;
  40. }
上面这些函数主要包含即调用关系如下,
kset_create_and_add
kset_create
kzalloc
kset_register
kset_init
kobject_add_internal

kset中嵌入了一个kobject,所以还有一些和kobject相似的函数如,
增加kset的引用,实际是调用kobject_get增加kset中的kobject的引用

点击(此处)折叠或打开

  1. static inline struct kset *kset_get(struct kset *k)
  2. {
  3.     return k ? to_kset(kobject_get(&k->kobj)) : NULL;    
  4. }
减少kset的引用,实际是调用kobject_put减少kset中的kobject的引用

点击(此处)折叠或打开

  1. static inline void kset_put(struct kset *k)
  2. {
  3.     kobject_put(&k->kobj);
  4. }
顺便提一下,子系统subsystem,在新的内核中已经没有这个结构了。在原来的内核中它用来表示比kset更高一层的容器,kset应该属于一个子系统,子系统帮助内核在分层结构中定位kset。内核子系统包括 block_subsys(/sys/block 块设备)、 devices_subsys(/sys/devices 核心设备层)。现在subsystem已经被kset代替了。

五,将kobject注册进sysfs系统
我们在看一下如何将kobject注册进sysfs系统中。使用函数kobject_init_and_add()(lib/kobject.c)函数将一个kobject注册进sysfs系统,在/sys中表现为生成一个相应的目录。

点击(此处)折叠或打开

  1. /**
  2.  * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
  3.  * @kobj: pointer to the kobject to initialize
  4.  * @ktype: pointer to the ktype for this kobject.
  5.  * @parent: pointer to the parent of this kobject.
  6.  * @fmt: the name of the kobject.
  7.  *
  8.  * This function combines the call to kobject_init() and
  9.  * kobject_add(). The same type of error handling after a call to
  10.  * kobject_add() and kobject lifetime rules are the same here.
  11.  */
  12. int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
  13.              struct kobject *parent, const char *fmt, ...)
  14. {
  15.     va_list args;
  16.     int retval;

  17.     kobject_init(kobj, ktype);            //调用初始化函数先初始化kobject变量

  18.     va_start(args, fmt);            //解析可变参数列表
  19.     retval = kobject_add_varg(kobj, parent, fmt, args); //给kobject添加参数,并且将其添加到sysfs系统。
  20.     va_end(args);                            //结束解析参数列表

  21.     return retval;
  22. }

点击(此处)折叠或打开

  1. static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs)
  2. {
  3.     int retval;

  4.     retval = kobject_set_name_vargs(kobj, fmt, vargs);        //设置kobject的名称
  5.     if (retval) {
  6.         printk(KERN_ERR "kobject: can not set name properly!\n");
  7.         return retval;
  8.     }
  9.     kobj->parent = parent;            //设置kobject的父kobject
  10.     return kobject_add_internal(kobj);    //添加kobject
  11. }

点击(此处)折叠或打开

  1. static int kobject_add_internal(struct kobject *kobj)
  2. {
  3.     int error = 0;
  4.     struct kobject *parent;

  5.     if (!kobj)        //检查是否为空
  6.         return -ENOENT;

  7.     if (!kobj->name || !kobj->name[0]) {    //kobj是否有名称,如果没有则不能注册,生成目录。
  8.         WARN(1, "kobject: (%p): attempted to be registered with empty "
  9.              "name!\n", kobj);
  10.         return -EINVAL;
  11.     }

  12.     parent = kobject_get(kobj->parent);    //获得父kobject,并增加父kobject的引用计数

  13.     /* join kset if set, use it as parent if we do not already have one */
  14.     if (kobj->kset) {                                        //是否有kset集合
  15.         if (!parent)                                            //如果没有父kobject则用kset中的kobject对象
  16.             parent = kobject_get(&kobj->kset->kobj);
  17.         kobj_kset_join(kobj);            //将kobject添加进它关联的kset的list链表中。
  18.         kobj->parent = parent;        //设置父koject
  19.     }

  20.     pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
  21.          kobject_name(kobj), kobj, __func__,
  22.          parent ? kobject_name(parent) : "",
  23.          kobj->kset ? kobject_name(&kobj->kset->kobj) : "");

  24.     error = create_dir(kobj);                //创建kobject的相应目录
  25.     
  26.     if (error) {        //创建时出错处理
  27.         kobj_kset_leave(kobj);
  28.         kobject_put(parent);
  29.         kobj->parent = NULL;

  30.         /* be noisy on error issues */
  31.         if (error == -EEXIST)
  32.             printk(KERN_ERR "%s failed for %s with "
  33.              "-EEXIST, don't try to register things with "
  34.              "the same name in the same directory.\n",
  35.              __func__, kobject_name(kobj));
  36.         else
  37.             printk(KERN_ERR "%s failed for %s (%d)\n",
  38.              __func__, kobject_name(kobj), error);
  39.         dump_stack();
  40.     } else
  41.         kobj->state_in_sysfs = 1;        //标记为已经注册进sysfs

  42.     return error;
  43. }
sysfs创建目录函数create_dir,在lib/kobject.c

点击(此处)折叠或打开

  1. static int create_dir(struct kobject *kobj)
  2. {
  3.     int error = 0;
  4.     if (kobject_name(kobj)) {
  5.         error = sysfs_create_dir(kobj);            //在sysfs中创建目录,将来有时间了可以分析下sysfs子系统。
  6.         if (!error) {
  7.             error = populate_dir(kobj);
  8.             if (error)
  9.                 sysfs_remove_dir(kobj);
  10.         }
  11.     }
  12.     return error;
  13. }
以上函数的主要调用关系,如下
kobject_init_and_add
kobject_init
kobject_add_varg
kobject_add_internal
create_dir
还有一个函数kobject_add,也可以添加一个kobject,它只是没有kobject_init这一步。

点击(此处)折叠或打开

  1. int kobject_add(struct kobject *kobj, struct kobject *parent,
  2.         const char *fmt, ...)
  3. {
  4.     va_list args;
  5.     int retval;

  6.     if (!kobj)
  7.         return -EINVAL;

  8.     if (!kobj->state_initialized) {
  9.         printk(KERN_ERR "kobject '%s' (%p): tried to add an "
  10.          "uninitialized object, something is seriously wrong.\n",
  11.          kobject_name(kobj), kobj);
  12.         dump_stack();
  13.         return -EINVAL;
  14.     }
  15.     va_start(args, fmt);
  16.     retval = kobject_add_varg(kobj, parent, fmt, args);
  17.     va_end(args);

  18.     return retval;
  19. }








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