Linux 设备模型之 (kobject、kset 和 Subsystem)(二)

1、kobject 结构

在Linux内核里,kobject是组成Linux设备模型的基础,一个kobject对应sysfs里的一个目录。从面向对象的角度来说,kobject可以看作是所有设备对象的基类,因为C语言并没有面向对象的语法,所以一般是把kobject内嵌到其他结构体里来实现类似的作用,这里的其他结构体可以看作是kobject的派生类。Kobject为Linux设备模型提供了很多有用的功能,比如引用计数,接口抽象,父子关系等等。引用计数本质上就是利用kref实现的。

另外,Linux设备模型还有一个重要的数据结构kset。Kset本身也是一个kobject,所以它在sysfs里同样表现为一个目录,但它和kobject的不同之处在于kset可以看作是一个容器,如果你把它类比为C++里的容器类如list也无不可。Kset之所以能作为容器来使用,其内部正是内嵌了一个双向链表结构struct list_head。

kobject 在内核中的描述

[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. };  

内核里的设备之间是以树状形式组织的,在这种组织架构里比较靠上层的节点可以看作是下层节点的父节点,反映到sysfs里就是上级目录和下级目录之间的关系, 在内核里,正是kobject帮助我们实现这种父子关系 。在kobject的定义里,name表示的是kobject在sysfs中的名字;指针parent用来指向kobject的父对象;Kref大家应该比较熟悉了,kobject通过它来实现引用计数;Kset指针用来指向这个kobject所属的kset,下文会再详细描述kset的用法;对于ktype,如果只是望文生义的话,应该是用来描述kobject的类型信息。Ktype的定义如下:

[cpp]  view plain  copy
  1. struct kobj_type {  
  2.     void (*release)(struct kobject *kobj);  
  3.     const struct sysfs_ops *sysfs_ops;  
  4.     struct attribute **default_attrs;  
  5. };  
函数指针release是给kref使用的, 当引用计数为0这个指针指向的函数会被调用来释放内存 。sysfs_ops和attribute是做什么用的呢?前文里提到,一个kobject对应sysfs里的一个目录, 而目录下的文件就是由sysfs_ops和attribute来实现的,其中,attribute定义了kobject的属性,在sysfs里对应一个文件,sysfs_ops用来定义读写这个文件的方法。 Ktype里的attribute是默认的属性,另外也可以使用更加灵活的手段,本文的重点还是放在default attribute。
[cpp]  view plain  copy
  1. #include <linux/kernel.h>  
  2. #include <linux/module.h>  
  3. #include <linux/slab.h>  
  4. #include <linux/kobject.h>  
  5.   
  6. struct my_kobj {   //内嵌kobject的结构  
  7.     int val;  
  8.     struct kobject kobj;  
  9. };  
  10.   
  11. struct my_kobj *obj1, *obj2;  
  12. struct kobj_type my_type;  
  13.   
  14. struct attribute name_attr = {  
  15.     .name = "name"//文件名  
  16.     .mode = 0444,  //指定文件的访问权限  
  17. };  
  18.   
  19. struct attribute val_attr = {  
  20.     .name = "val"//文件名  
  21.     .mode = 0666, //指定文件的访问权限  
  22. };  
  23.   
  24. struct attribute *my_attrs[] = {  
  25.     &name_attr,   
  26.     &val_attr,  
  27.     NULL,  
  28. };  
  29.   
  30. /* 
  31. 结构体struct attribute里的name变量用来指定文件名,mode变量用来指定文件的访问权限。 
  32. 这里需要着重指出的是,数组my_attrs的最后一项一定要赋为NULL,否则会造成内核oops。 
  33. */  
  34. ssize_t my_show(struct kobject *kobj, struct attribute *attr, char *buffer)  
  35. {  
  36.     struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj);  
  37.     ssize_t count = 0;  
  38.   
  39.     if (strcmp(attr->name, "name") == 0) {  
  40.         count = sprintf(buffer, "%s\n", kobject_name(kobj));  
  41.     } else if (strcmp(attr->name, "val") == 0) {  
  42.         count = sprintf(buffer, "%d\n", obj->val);  
  43.     }  
  44.   
  45.     return count;  
  46. }  
  47.   
  48. ssize_t my_store(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size)  
  49. {  
  50.     struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj);  
  51.   
  52.     if (strcmp(attr->name, "val") == 0) {  
  53.         sscanf(buffer, "%d", &obj->val);  
  54.     }  
  55.   
  56.     return size;  
  57. }  
  58.   
  59. struct sysfs_ops my_sysfsops = {  
  60.     .show = my_show,  
  61.     .store = my_store,  
  62. };  
  63.   
  64. void obj_release(struct kobject *kobj)  
  65. {  
  66.     struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj);  
  67.     printk(KERN_INFO "obj_release %s\n", kobject_name(&obj->kobj));  
  68.     kfree(obj);  
  69. }  
  70.   
  71. static int __init mykobj_init(void)  
  72. {  
  73.     printk(KERN_INFO "mykobj_init\n");  
  74.   
  75.     obj1 = kzalloc(sizeof(struct my_kobj), GFP_KERNEL); //分配obj1和obj2并赋值  
  76.     if (!obj1) {  
  77.         return -ENOMEM;  
  78.     }  
  79.     obj1->val = 1;  
  80.   
  81.     obj2 = kzalloc(sizeof(struct my_kobj), GFP_KERNEL);  
  82.     if (!obj2) {  
  83.         kfree(obj1);  
  84.         return -ENOMEM;  
  85.     }  
  86.     obj2->val = 2;  
  87.   
  88.     my_type.release = obj_release;  
  89.     my_type.default_attrs = my_attrs;  
  90.     my_type.sysfs_ops = &my_sysfsops;  
  91.   
  92.     kobject_init_and_add(&obj1->kobj, &my_type, NULL, "mykobj1"); /*函数来初始化kobject并把它加入到设备模型的体系架构*/  
  93.     kobject_init_and_add(&obj2->kobj, &my_type, &obj1->kobj, "mykobj2");  
  94. /* 
  95.     kobject_init用来初始化kobject结构,kobject_add用来把kobj加入到设备模型之中。 
  96.     在实作中,我们先对obj1进行初始化和添加的动作,调用参数里,parent被赋为NULL,表示obj1没有父对象,反映到sysfs里, 
  97.     my_kobj1的目录会出现在/sys下,obj2的父对象设定为obj1,那么my_kobj2的目录会出现在/sys/my_kobj1下面。 
  98.     前面提到,kobject也提供了引用计数的功能,虽然本质上是利用kref,但也提供了另外的接口供用户使用。 
  99.     kobject_init_and_add和kobject_init这两个函数被调用后,kobj的引用计数会初始化为1, 
  100.     所以在module_exit时要记得用kobject_put来释放引用计数。 
  101. */    
  102.     return 0;  
  103. }  
  104.   
  105. static void __exit mykobj_exit(void)  
  106. {  
  107.     printk(KERN_INFO "mykobj_exit\n");  
  108.   
  109.     kobject_del(&obj2->kobj); /*先子对象,后父对象*/  
  110.     kobject_put(&obj2->kobj);  
  111.       
  112.     kobject_del(&obj1->kobj);  
  113.     kobject_put(&obj1->kobj);  
  114.   
  115.     return;  
  116. }  
  117. /* 
  118. kobject_del的作用是把kobject从设备模型的那棵树里摘掉,同时sysfs里相应的目录也会删除。 
  119. 这里需要指出的是,释放的顺序应该是先子对象,后父对象。 
  120. 因为kobject_init_and_add和kobject_add这两个函数会调用kobject_get来增加父对象的引用计数, 
  121. 所以kobject_del需要调用kobject_put来减少父对象的引用计数。在本例中,如果先通过kobject_put来释放obj1, 
  122. 那kobject_del(&obj2->kobj)就会出现内存错误。 
  123. */  
  124. module_init(mykobj_init);  
  125. module_exit(mykobj_exit);  
  126.   
  127. MODULE_LICENSE("GPL");  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值