Kobject模型

转载自:http://blog.chinaunix.net/uid-11319766-id-3253414.html
这篇文章太重要了,所以必须转载。感谢作者
一、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. }  
  7.   
  8. void kref_get(struct kref *kref)  
  9. {  
  10.     WARN_ON(!atomic_read(&kref->refcount));  
  11.     atomic_inc(&kref->refcount);    //将kref中的这个原子变量加1  
  12.     smp_mb__after_atomic_inc();  
  13. }  
减少一个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.  
  3.      * kset_create - create a struct kset dynamically 
  4.      * 
  5.      * @name: the name for the kset 
  6.      * @uevent_ops: a struct kset_uevent_ops for the kset 
  7.      * @parent_kobj: the parent kobject of this kset, if any. 
  8.      * 
  9.      * This function creates a kset structure dynamically. This structure can 
  10.      * then be registered with the system and show up in sysfs with a call to 
  11.      * kset_register(). When you are finished with this structure, if 
  12.      * kset_register() has been called, call kset_unregister() and the 
  13.      * structure will be dynamically freed when it is no longer being used. 
  14.      * 
  15.      * If the kset was not able to be created, NULL will be returned. 
  16.      */  
  17.     static struct kset *kset_create(const char *name,  
  18.                     struct kset_uevent_ops *uevent_ops,  
  19.                     struct kobject *parent_kobj)  
  20.     {  
  21.         struct kset *kset;  
  22.         int retval;  
  23.         kset = kzalloc(sizeof(*kset), GFP_KERNEL);  
  24.         if (!kset)  
  25.             return NULL;  
  26.         retval = kobject_set_name(&kset->kobj, name);    //设置kobject名称  
  27.         if (retval) {  
  28.             kfree(kset);  
  29.             return NULL;  
  30.         }  
  31.         kset->uevent_ops = uevent_ops;  
  32.         kset->kobj.parent = parent_kobj;            //设置kset的kobject的父对象  
  33.         /* 
  34.          * The kobject of this kset will have a type of kset_ktype and belong to 
  35.          * no kset itself. That way we can properly free it when it is 
  36.          * finished being used. 
  37.          */  
  38.         kset->kobj.ktype = &kset_ktype;    //设置kset的kobject的默认属性  
  39.         kset->kobj.kset = NULL;  
  40.         return kset;  
  41.     }  

上面这些函数主要包含即调用关系如下,
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.     {  
  4.         int error = 0;  
  5.         struct kobject *parent;  
  6.         if (!kobj)        //检查是否为空  
  7.             return -ENOENT;  
  8.         if (!kobj->name || !kobj->name[0]) {    //kobj是否有名称,如果没有则不能注册,生成目录。  
  9.             WARN(1, "kobject: (%p): attempted to be registered with empty "  
  10.                  "name!\n", kobj);  
  11.             return -EINVAL;  
  12.         }  
  13.         parent = kobject_get(kobj->parent);    //获得父kobject,并增加父kobject的引用计数  
  14.         /* join kset if set, use it as parent if we do not already have one */  
  15.         if (kobj->kset) {                                        //是否有kset集合  
  16.             if (!parent)                                            //如果没有父kobject则用kset中的kobject对象  
  17.                 parent = kobject_get(&kobj->kset->kobj);  
  18.             kobj_kset_join(kobj);            //将kobject添加进它关联的kset的list链表中。  
  19.             kobj->parent = parent;        //设置父koject  
  20.         }  
  21.         pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",  
  22.              kobject_name(kobj), kobj, __func__,  
  23.              parent ? kobject_name(parent) : "<NULL>",  
  24.              kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");  
  25.         error = create_dir(kobj);                //创建kobject的相应目录  
  26.           
  27.         if (error) {        //创建时出错处理  
  28.             kobj_kset_leave(kobj);  
  29.             kobject_put(parent);  
  30.             kobj->parent = NULL;  
  31.             /* be noisy on error issues */  
  32.             if (error == -EEXIST)  
  33.                 printk(KERN_ERR "%s failed for %s with "  
  34.                  "-EEXIST, don't try to register things with "  
  35.                  "the same name in the same directory.\n",  
  36.                  __func__, kobject_name(kobj));  
  37.             else  
  38.                 printk(KERN_ERR "%s failed for %s (%d)\n",  
  39.                  __func__, kobject_name(kobj), error);  
  40.             dump_stack();  
  41.         } else  
  42.             kobj->state_in_sysfs = 1;        //标记为已经注册进sysfs  
  43.         return error;  
  44.     }  
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;  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值