Linux设备模型分析之kobject

来源:互联网 发布:订餐的软件 编辑:程序博客网 时间:2024/05/21 19:42
一、kobject应用举例
Linux设备模型最基本的组成元素是kobject,我们先来看一个kobject的应用例子,该程序在Ubuntu 10.10, 2.6.32-38-generic-pae内核上调试通过。
[cpp] view plain copy
  1. #include <linux/device.h>  
  2. #include <linux/module.h>  
  3. #include <linux/kernel.h>  
  4. #include <linux/init.h>  
  5. #include <linux/string.h>  
  6. #include <linux/sysfs.h>  
  7. #include <linux/stat.h>  
  8.   
  9. MODULE_AUTHOR("haoyu");  
  10. MODULE_LICENSE("Dual BSD/GPL");  
  11.   
  12. struct my_kobject  
  13. {  
  14.     int value;  
  15.     struct kobject kobj;  
  16. };  
  17.       
  18. struct my_kobject my_kobj;  
  19.   
  20. void kobject_release(struct kobject *kobject);  
  21. ssize_t kobject_attr_show(struct kobject *kobject, struct attribute *attr,char *buf);  
  22. ssize_t kobject_attr_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count);  
  23.   
  24. struct attribute kobject_attr1 = {  
  25.     .name = "name",  
  26.     .mode = S_IRWXUGO,  
  27. };  
  28.   
  29. struct attribute kobject_attr2 = {  
  30.     .name = "value",  
  31.     .mode = S_IRWXUGO,  
  32. };  
  33.   
  34. static struct attribute *kobject_def_attrs[] = {  
  35.     &kobject_attr1,  
  36.     &kobject_attr2,  
  37.     NULL,  
  38. };  
  39.   
  40. struct sysfs_ops kobject_sysfs_ops =  
  41. {  
  42.     .show = kobject_attr_show,  
  43.     .store = kobject_attr_store,  
  44. };  
  45.   
  46. struct kobj_type ktype =  
  47. {  
  48.     .release = kobject_release,  
  49.     .sysfs_ops = &kobject_sysfs_ops,  
  50.     .default_attrs = kobject_def_attrs,  
  51. };  
  52.   
  53. void kobject_release(struct kobject *kobject)  
  54. {  
  55.     printk("kobject release.\n");  
  56. }  
  57.   
  58. ssize_t kobject_attr_show(struct kobject *kobject, struct attribute *attr,char *buf)  
  59. {  
  60.     int count = 0;  
  61.     struct my_kobject *my_kobj = container_of(kobject, struct my_kobject, kobj);  
  62.     printk("kobject attribute show.\n");  
  63.     if(strcmp(attr->name, "name") == 0)  
  64.         count = sprintf(buf, "%s\n", kobject->name);  
  65.     else if(strcmp(attr->name, "value") == 0)  
  66.         count = sprintf(buf, "%d\n", my_kobj->value);  
  67.     else  
  68.         printk("no this attribute.\n");  
  69.       
  70.     return count;  
  71. }  
  72.   
  73. ssize_t kobject_attr_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count)  
  74. {  
  75.     int val;  
  76.     struct my_kobject *my_kobj = container_of(kobject, struct my_kobject, kobj);  
  77.     printk("kobject attribute store.\n");  
  78.     if(strcmp(attr->name, "name") == 0)  
  79.         printk("Can not change name.\n");  
  80.     else if(strcmp(attr->name, "value") == 0)  
  81.     {  
  82.         val = buf[0] - '0';  
  83.         if(val == 0 || val == 1)  
  84.             my_kobj->value = val;  
  85.         else  
  86.             printk("value is '0' or '1'\n");  
  87.     }  
  88.     else  
  89.         printk("no this attribute.\n");  
  90.           
  91.     return count;  
  92. }  
  93.   
  94. static int kobject_test_init(void)  
  95. {  
  96.     printk("kboject test init.\n");  
  97.     kobject_init_and_add(&my_kobj.kobj,&ktype,NULL,"kobject_test");  
  98.     return 0;  
  99. }  
  100.   
  101. static void kobject_test_exit(void)  
  102. {  
  103.     printk("kobject test exit.\n");  
  104.     kobject_del(&my_kobj.kobj);  
  105. }  
  106.   
  107. module_init(kobject_test_init);  
  108. module_exit(kobject_test_exit);  
该模块执行过程如下图所示:
 
二、相关数据结构:
kobject是Linux设备模型中最基本的数据结构,代表设备模式的一个基本对象。
kobj_type是kobject的类型,包括kobject的属性以及属性的操作接口,不同的kobject可以具有相同的kobj_type。
kset是几个kobject的集合,这些kobject可以具有相同的kobj_type,也可以具有不同的kobj_type。
[cpp] view plain copy
  1. struct kobject {  
  2.         const char             *name;  
  3.         struct list_head       entry;  
  4.         struct kobject         *parent;  
  5.         struct kset            *kset;  
  6.         struct kobj_type       *ktype;  
  7.         struct sysfs_dirent    *sd;  
  8.         struct kref            kref;  
  9.         unsigned int state_initialized:1;  
  10.         unsigned int state_in_sysfs:1;  
  11.         unsigned int state_add_uevent_sent:1;  
  12.         unsigned int state_remove_uevent_sent:1;  
  13.         unsigned int uevent_suppress:1;  
  14. };  
  15.    
  16. /** 
  17.  * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. 
  18.  * 
  19.  * A kset defines a group of kobjects.  They can be individually 
  20.  * different "types" but overall these kobjects all want to be grouped 
  21.  * together and operated on in the same manner.  ksets are used to 
  22.  * define the attribute callbacks and other common events that happen to 
  23.  * a kobject. 
  24.  * 
  25.  * @list: the list of all kobjects for this kset 
  26.  * @list_lock: a lock for iterating over the kobjects 
  27.  * @kobj: the embedded kobject for this kset (recursion, isn't it fun...) 
  28.  * @uevent_ops: the set of uevent operations for this kset.  These are 
  29.  * called whenever a kobject has something happen to it so that the kset 
  30.  * can add new environment variables, or filter out the uevents if so 
  31.  * desired. 
  32.  */  
  33. struct kset {  
  34.         struct list_head list;  
  35.         spinlock_t list_lock;  
  36.         struct kobject kobj;  
  37.         const struct kset_uevent_ops *uevent_ops;  
  38. };  
  39.    
  40. struct kset_uevent_ops {  
  41.         int (* const filter)(struct kset *kset, struct kobject *kobj);  
  42.         const char *(* const name)(struct kset *kset, struct kobject *kobj);  
  43.         int (* const uevent)(struct kset *kset, struct kobject *kobj,  
  44.                      struct kobj_uevent_env *env);  
  45. };  
  46.    
  47. struct kobj_type {  
  48.         void (*release)(struct kobject *kobj);  
  49.         const struct sysfs_ops *sysfs_ops;  
  50.         struct attribute **default_attrs;  
  51.         const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);  
  52.         const void *(*namespace)(struct kobject *kobj);  
  53. };  
  54.    
  55. struct sysfs_ops {  
  56.         ssize_t (*show)(struct kobject *, struct attribute *,char *);  
  57.         ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);  
  58. };  
  59.    
  60. struct attribute {  
  61.         const char             *name;  
  62.         mode_t                 mode;  
  63. #ifdef CONFIG_DEBUG_LOCK_ALLOC  
  64.         struct lock_class_key  *key;  
  65.         struct lock_class_key  skey;  
  66. #endif  
  67. };  
  68.    
  69. /* 
  70.  * Callbacks so sysfs can determine namespaces 
  71.  *   @current_ns: return calling task's namespace 
  72.  *   @netlink_ns: return namespace to which a sock belongs (right?) 
  73.  *   @initial_ns: return the initial namespace (i.e. init_net_ns) 
  74.  */  
  75. struct kobj_ns_type_operations {  
  76.         enum kobj_ns_type type;  
  77.         const void *(*current_ns)(void);  
  78.         const void *(*netlink_ns)(struct sock *sk);  
  79.         const void *(*initial_ns)(void);  
  80. };  
  81.    
  82. struct kref {  
  83.         atomic_t refcount;  
  84. };  
 
三、kobject注册和注销过程分析
kobject的注册是通过调用kobject_init_and_add函数,该函数定义如下:
[cpp] view plain copy
  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.    
  18.         kobject_init(kobj, ktype);  
  19.    
  20.         va_start(args, fmt);  
  21.         retval = kobject_add_varg(kobj, parent, fmt, args);  
  22.         va_end(args);  
  23.    
  24.         return retval;  
  25. }  
这个函数分为两部分,首先调用kobject_init函数对kobject对象进行基本的初始化。然后,调用kobject_add_varg函数将kobject注册到系统中。va_start和va_end是处理可变参数的固定语法。
先来看kobject_init,该函数定义如下:
[cpp] view plain copy
  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.    
  17.         if (!kobj) {  
  18.                err_str = "invalid kobject pointer!";  
  19.                goto error;  
  20.         }  
  21.         if (!ktype) {  
  22.                err_str = "must have a ktype to be initialized properly!\n";  
  23.                goto error;  
  24.         }  
  25.         if (kobj->state_initialized) {  
  26.                /* do not error out as sometimes we can recover */  
  27.                printk(KERN_ERR "kobject (%p): tried to init an initialized "  
  28.                       "object, something is seriously wrong.\n", kobj);  
  29.                 dump_stack();  
  30.         }  
  31.    
  32.         kobject_init_internal(kobj);  
  33.         kobj->ktype = ktype;  
  34.         return;  
  35.    
  36. error:  
  37.         printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);  
  38.         dump_stack();  
  39. }  
该函数首先确保kobj和ktype都存在,否则直接退出。如果该kobj进行过初始化,则打印警告信息。然后调用kobject_init_internal真正开始初始化kobj,最后把kobj->ktype设置为ktype。
kobject_init_internal函数定义如下:
[cpp] view plain copy
  1. static void kobject_init_internal(struct kobject *kobj)  
  2. {  
  3.         if (!kobj)  
  4.                return;  
  5.         kref_init(&kobj->kref);  
  6.         INIT_LIST_HEAD(&kobj->entry);  
  7.         kobj->state_in_sysfs = 0;  
  8.         kobj->state_add_uevent_sent = 0;  
  9.         kobj->state_remove_uevent_sent = 0;  
  10.         kobj->state_initialized = 1;  
  11. }  
首先初始化kobj->kref,实际上kobj->kref就是一个原子变量(atomic_t)。接着初始化链表项kobj->entry,并设置其他kobject成员。
至此,kobject_init函数就分析完了,我们返回到kobject_init_and_add函数,下面该分析kobject_add_varg函数了:
[cpp] view plain copy
  1. static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,  
  2.                            const char *fmt, va_list vargs)  
  3. {  
  4.         int retval;  
  5.    
  6.         retval = kobject_set_name_vargs(kobj, fmt, vargs);  
  7.         if (retval) {  
  8.                printk(KERN_ERR "kobject: can not set name properly!\n");  
  9.                return retval;  
  10.         }  
  11.         kobj->parent = parent;  
  12.         return kobject_add_internal(kobj);  
  13. }  
首先调用kobject_set_name_vargs设置kob->name。然后初始化kobj->parent为parent参数指定的kobject。最后,调用kobject_add_internal将kobject注册到系统中,该函数定义如下:
[cpp] view plain copy
  1. static int kobject_add_internal(struct kobject *kobj)  
  2. {  
  3.         int error = 0;  
  4.         struct kobject *parent;  
  5.    
  6.         if (!kobj)  
  7.                return -ENOENT;  
  8.    
  9.         if (!kobj->name || !kobj->name[0]) {  
  10.                WARN(1, "kobject: (%p): attempted to be registered with empty "  
  11.                         "name!\n", kobj);  
  12.                return -EINVAL;  
  13.         }  
  14.    
  15.         parent = kobject_get(kobj->parent);  
  16.    
  17.         /* join kset if set, use it as parent if we do not already have one */  
  18.         if (kobj->kset) {  
  19.                if (!parent)  
  20.                        parent = kobject_get(&kobj->kset->kobj);  
  21.                kobj_kset_join(kobj);  
  22.                kobj->parent = parent;  
  23.         }  
  24.    
  25.         pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",  
  26.                 kobject_name(kobj), kobj, __func__,  
  27.                 parent ? kobject_name(parent) : "<NULL>",  
  28.                 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");  
  29.    
  30.         error = create_dir(kobj);  
  31.         if (error) {  
  32.                kobj_kset_leave(kobj);  
  33.                kobject_put(parent);  
  34.                kobj->parent = NULL;  
  35.    
  36.                /* be noisy on error issues */  
  37.                if (error == -EEXIST)  
  38.                        printk(KERN_ERR "%s failed for %s with "  
  39.                               "-EEXIST, don't try to register things with "  
  40.                               "the same name in the same directory.\n",  
  41.                               __func__, kobject_name(kobj));  
  42.                else  
  43.                        printk(KERN_ERR "%s failed for %s (%d)\n",  
  44.                               __func__, kobject_name(kobj), error);  
  45.                dump_stack();  
  46.         } else  
  47.                kobj->state_in_sysfs = 1;  
  48.    
  49.         return error;  
  50. }  
首先确保kobj->name已经被赋值,即kobject必须有名字。如果指定了kobj->kset,则调用kobj_kset_join将kobj加入到kobj->kset中。同时,如果kobj->parent仍为NULL,则将kobj->parent设置为kobj->kset->kobj。
然后,调用create_dir(kobj)在/sys目录下建立kobject相关目录结构。
kobj_kset_join函数定义如下:
[cpp] view plain copy
  1. /* add the kobject to its kset's list */  
  2. static void kobj_kset_join(struct kobject *kobj)  
  3. {  
  4.         if (!kobj->kset)  
  5.                return;  
  6.    
  7.         kset_get(kobj->kset);  
  8.         spin_lock(&kobj->kset->list_lock);  
  9.         list_add_tail(&kobj->entry, &kobj->kset->list);  
  10.         spin_unlock(&kobj->kset->list_lock);  
  11. }  
create_dir函数定义如下:
[cpp] view plain copy
  1. static int create_dir(struct kobject *kobj)  
  2. {  
  3.         int error = 0;  
  4.         if (kobject_name(kobj)) {  
  5.                error = sysfs_create_dir(kobj);  
  6.                if (!error) {  
  7.                        error = populate_dir(kobj);  
  8.                        if (error)  
  9.                                sysfs_remove_dir(kobj);  
  10.                }  
  11.         }  
  12.         return error;  
  13. }  
首先调用sysfs_create_dir在/sys下建立目录,然后再调用populate_dir在新建目录下生成属性文件。
sysfs_create_dir函数定义如下:
[cpp] view plain copy
  1. /** 
  2.  *      sysfs_create_dir - create a directory for an object. 
  3.  *      @kobj:         object we're creating directory for.  
  4.  */  
  5. int sysfs_create_dir(struct kobject * kobj)  
  6. {  
  7.         enum kobj_ns_type type;  
  8.         struct sysfs_dirent *parent_sd, *sd;  
  9.         const void *ns = NULL;  
  10.         int error = 0;  
  11.    
  12.         BUG_ON(!kobj);  
  13.    
  14.         if (kobj->parent)  
  15.                parent_sd = kobj->parent->sd;  
  16.         else  
  17.                parent_sd = &sysfs_root;  
  18.    
  19.         if (sysfs_ns_type(parent_sd))  
  20.                ns = kobj->ktype->namespace(kobj);  
  21.         type = sysfs_read_ns_type(kobj);  
  22.    
  23.         error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd);  
  24.         if (!error)  
  25.                kobj->sd = sd;  
  26.         return error;  
  27. }  
这里主要是通过调用create_dir函数建立kobject对应的目录。这个函数我们就不继续向下跟踪了。
下面来看populate_dir函数,其定义如下:
[cpp] view plain copy
  1. /* 
  2.  * populate_dir - populate directory with attributes. 
  3.  * @kobj: object we're working on. 
  4.  * 
  5.  * Most subsystems have a set of default attributes that are associated 
  6.  * with an object that registers with them.  This is a helper called during 
  7.  * object registration that loops through the default attributes of the 
  8.  * subsystem and creates attributes files for them in sysfs. 
  9.  */  
  10. static int populate_dir(struct kobject *kobj)  
  11. {  
  12.         struct kobj_type *t = get_ktype(kobj);  
  13.         struct attribute *attr;  
  14.         int error = 0;  
  15.         int i;  
  16.    
  17.         if (t && t->default_attrs) {  
  18.                for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {  
  19.                        error = sysfs_create_file(kobj, attr);  
  20.                        if (error)  
  21.                                break;  
  22.                }  
  23.         }  
  24.         return error;  
  25. }  
该函数循环遍历kobject的所有属性,并调用sysfs_create_file函数在/sys系统对应目录下建立属性文件。
至此,kobject的注册过程我们就分析完了。
kobject的注销过程是调用kobject_del函数,该函数定义如下:
[cpp] view plain copy
  1. /** 
  2.  * kobject_del - unlink kobject from hierarchy. 
  3.  * @kobj: object. 
  4.  */  
  5. void kobject_del(struct kobject *kobj)  
  6. {  
  7.         if (!kobj)  
  8.                return;  
  9.    
  10.         sysfs_remove_dir(kobj);  
  11.         kobj->state_in_sysfs = 0;  
  12.         kobj_kset_leave(kobj);  
  13.         kobject_put(kobj->parent);  
  14.         kobj->parent = NULL;  
  15. }  
这里需要注意的是,只要把目录删除,目录下的属性文件自动就删除了。
0 0