Linux设备模型(上)之底层模型

努力成为linux kernel hacker的人李万鹏原创作品,转载请标明出处

http://blog.csdn.net/woshixingaaa/archive/2011/05/05/6396618.aspx

如果哪里有理解不对的请指教,文章引用的内核源码版本为2.6.29.1的。

建立设备模型主要为了管理方便。最初引入设备模型是为了电源管理。建立一个全局的设备树(device tree),当系统进入休眠时,系统可以通过这棵树找到所有的设备,随时让他们挂起(suspend)或者唤醒(resume)。

2.6版内核提供的功能:

电源管理和系统关机

       完成这些工作需要对一些系统结构的理解。比如一个USB宿主适配器,在处理完所有与其连接的设备前是不能被关闭的。设备模型使得操作系统能够以正确的顺序遍历硬件。

与用户空间通信

       sysfs虚拟文件系统的实现与设备模型密切相关,并且向外界展示了它所表示的结构。向用户空间所提供的系统信息,以及改变操作参数的接口,将越来越多的通过sysfs实现,也就是说通过设备模型实现。

热插拔设备

       内核中的热插拔机制可以处理热插拔设备,特别是能够与用户空间进行关于热插拔设备的通信,而这种机制也是通过热插拔管理的。   

设备类型

       把设备分门别类有助于设备的管理与使用。比如要找USB鼠标,只要去classes/input/里去找就可以了,而不必关心这个鼠标是接到哪个USB主机控制器的哪个Hub的第几个端口上。

对象生命周期

       得有一个好的机制来实现设备生命周期的管理。比如把USB鼠标拔了之后,全局设备树和sysfs里面得相应去掉。

设备底层模型:

Linux设备模型的底层是数据结构kobject,内核用kobject结构将各个对象连接起来组成一个分层的结构体系,从而与模块化的子系统相匹配。一个kset是嵌入相同类型结构的kobject集合。kset和他的kobject的关系与下图类似,请记住:

  • ü         在图中所有被包含的kobject,实际上是被嵌入到其他类型中的,甚至可能是其他的kset。
  • ü         一个kobject的父节点不一定是包含它的kset。

 

Kobject是组成设备模型的基本结构,最初他只是被理解为一个简单的引用计数,但是随着时间的推移,他的任务越来越多,因此也有了许多成员,他的结构体如下:

[c-sharp]  view plain copy
  1. struct kobject {  
  2.     const char      *name;  
  3.     struct list_head    entry;       //挂接到所在kset中去的单元  
  4.     struct kobject      *parent; //指向父对象的指针  
  5.     struct kset     *kset;       //所属kset的指针   
  6.     struct kobj_type    *ktype;      //指向其对象类型描述符的指针  
  7.     struct sysfs_dirent *sd;     //sysfs文件系统中与该对象对应的目录实体的指针  
  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. };  

name指向设备的名字,entry,parent,kset就是用来形成树状结构的指针。Kobj_type *type用来表示该kobject的类型,struct sysfs_dirent类型的指针指向了该kobject在sysfs中的目录实体,sysfs中每一个dentry都会对应一个sysfs_dirent结构。每个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录。/sys是专为Linux设备模型建立的,kobject是帮助建立/sys文件系统的。每当我们新增一个kobject结构时,同时会在/sys下创建一个目录。这里隐藏了如下程序调用流程:kobject_add()->kobject_add_varg()->kobject_add_internal()->create_dir()->sysfs_new_dirent()。在sysfs_new_dirent()函数中通过slab分配了一个dirent(至于什么是dirent会在<Linux设备模型(下)之sysfs文件系统>中讲解),并返回给一个指向这个dirent的指针sd给create_dir(),在create_dir()函数中有这么一句:sd->s_dir.kobj = kobj;也就是让dirent的一个成员的域指向了他所对应的kobject,kobject中struct sysfs_dirent    *sd;又指向了dirent,所以kobject与sysfs死死的拥抱在一起,为了幸福的明天。在kobject_del()函数中会调用sysfs_remove_dir(),sysfs_remove_dir()中有这么一句:kobj->sd = NULL;表示kobject与sysfs_dirent的婚姻破裂了。

kobject的接口函数:

[c-sharp]  view plain copy
  1. void kobject_init(struct kobject *kobj);  

kobject初始化函数,设置kobject引用计数为1。

[c-sharp]  view plain copy
  1. int kobject_set_name(struct kobject *kobj, const char *format, …);  

设置kobject的名字。

[c-sharp]  view plain copy
  1. struct kobject *kobject_get(struct kobject *kobj);  
  2. struct kobject *kobject_put(struct kobject *kobj);  

减少和增加kobject的引用计数。

[c-sharp]  view plain copy
  1. extern int __must_check kobject_init_and_add(struct kobject *kobj,  
  2.                          struct kobj_type *ktype,  
  3.                          struct kobject *parent,  
  4.                          const char *fmt, ...);  

kobject注册函数,该函数只是kobjec_init和kobject_add_varg的简单组合。旧内核称为

[c-sharp]  view plain copy
  1. extern int kobject_register(struct kobject *kobj);  
[c-sharp]  view plain copy
  1. void kobject_del(struct kobject *kobj);  

从Linux设备层次中(hierarchy)中删除kobj对象。

[c-sharp]  view plain copy
  1. struct kset {  
  2.     struct list_head list;          //用于连接该kset中所有kobject的链表头  
  3.     spinlock_t list_lock;           //用于互斥访问  
  4.     struct kobject kobj;            //嵌入的kobject  
  5.     struct kset_uevent_ops *uevent_ops;  
  6. };  

包含在kset中的所有kobject被组织成一个双向循环链表,list真是该链表的链表头。kset数据结构还内嵌了一个kobject对象(由kobj表示),所有属于这个kset的kobject对象的parent域均指向这个内嵌的对象。此外,kset还依赖于kobj维护引用计数:kset的引用计数实际上就是内嵌的kobject对象的引用计数。

[c-sharp]  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.     const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);  
  6.     const void *(*namespace)(struct kobject *kobj);  
  7. };  

释放kobject使用release函数,release函数并没有包含在kobject自身内,他包含在与kobject相关联的kobj_type中。sysfs_ops是指向如何读写的函数的指针。

[c-sharp]  view plain copy
  1. struct sysfs_ops {  
  2.     ssize_t (*show)(struct kobject *, struct attribute *,char *);  
  3.     ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);  
  4. };  

show相当于read,store相当于write。

struct attribute **default_attrs;是属性数组。在sysfs中,kobject对应目录,kobject的属性对应这个目录下的文件。调用show和store函数来读写文件,就可以得到属性中的内容。

 

一个热插拔事件是从内核空间发送到用户空间的通知。它表明系统配置出现了变化。无论kobject被创建还是被删除,都会产生这种事件。比如,当数码相机通过USB线缆插入到系统时。热插拔事件会导致对/sbin/hotplug程序的调用,该程序通过加载驱动程序,创建设备节点,挂装分区,或者其他正确的动作来响应。对热插拔事件的控制由保存在 结构体中的函数完成:

 

[c-sharp]  view plain copy
  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,  
  5.               struct kobj_uevent_env *env);  
  6. };  

我们可以在kset结构的hotplug_ops成员中发现指向这个结构的指针。如果在kset中不包含一个指定的kobject,内核将在分层结构中进行搜索(通过parent指针),直到找到一个包含有kset的kobject为止,然后使用这个kset的热插拔操作。下面是一个测试的程序:

 kobject.c

[c-sharp]  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("David Xie");  
  10. MODULE_LICENSE("Dual BSD/GPL");  
  11.    
  12. void obj_test_release(struct kobject *kobject);  
  13. ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf);  
  14. ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count);  
  15.    
  16. struct attribute test_attr = {  
  17.         .name = "kobj_config",  
  18.         .mode = S_IRWXUGO,  
  19. };  
  20.    
  21. static struct attribute *def_attrs[] = {  
  22.         &test_attr,  
  23.         NULL,  
  24. };  
  25.    
  26.    
  27. struct sysfs_ops obj_test_sysops =  
  28. {  
  29.         .show = kobj_test_show,  
  30.         .store = kobj_test_store,  
  31. };  
  32.    
  33. struct kobj_type ktype =   
  34. {  
  35.         .release = obj_test_release,  
  36.         .sysfs_ops=&obj_test_sysops,  
  37.         .default_attrs=def_attrs,  
  38. };  
  39.    
  40. void obj_test_release(struct kobject *kobject)  
  41. {  
  42.         printk("eric_test: release ./n");  
  43. }  
  44.    
  45. ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf)  
  46. {  
  47.         printk("have show./n");  
  48.         printk("attrname:%s./n", attr->name);  
  49.         return strlen(attr->name)+2;  
  50. }  
  51.    
  52. ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count)  
  53. {  
  54.         printk("havestore/n");  
  55.         printk("write: %s/n",buf);  
  56.         return count;  
  57. }  
  58.    
  59. struct kobject kobj;  
  60. static int kobj_test_init()  
  61. {  
  62.         printk("kboject test init./n");  
  63.         kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");  
  64.         return 0;  
  65. }  
  66.    
  67. static int kobj_test_exit()  
  68. {  
  69.         printk("kobject test exit./n");  
  70.         kobject_del(&kobj);  
  71.         return 0;  
  72. }  
  73.    
  74. module_init(kobj_test_init);  
  75. module_exit(kobj_test_exit);  

kset.c

[c-sharp]  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. #include <linux/kobject.h>  
  9.    
  10. MODULE_AUTHOR("David Xie");  
  11. MODULE_LICENSE("Dual BSD/GPL");  
  12.    
  13. struct kset kset_p;  
  14. struct kset kset_c;  
  15.   
  16. int kset_filter(struct kset *kset, struct kobject *kobj)  
  17. {  
  18.         printk("Filter: kobj %s./n",kobj->name);  
  19.         return 1;  
  20. }  
  21.    
  22. const char *kset_name(struct kset *kset, struct kobject *kobj)  
  23. {  
  24.         static char buf[20];  
  25.         printk("Name: kobj %s./n",kobj->name);  
  26.         sprintf(buf,"%s","kset_name");  
  27.         return buf;  
  28. }  
  29.    
  30. int kset_uevent(struct kset *kset, struct kobject *kobj,struct kobj_uevent_env *env)  
  31. {  
  32.         int i = 0;  
  33.         printk("uevent: kobj %s./n",kobj->name);  
  34.   
  35.         while( i < env->envp_idx){  
  36.                 printk("%s./n",env->envp[i]);  
  37.                 i++;  
  38.         }  
  39.   
  40.         return 0;  
  41. }  
  42.   
  43. struct kset_uevent_ops uevent_ops =   
  44. {  
  45.         .filter = kset_filter,  
  46.         .name   = kset_name,  
  47.         .uevent = kset_uevent,  
  48. };  
  49.    
  50. int kset_test_init()  
  51. {  
  52.         printk("kset test init./n");  
  53.         kobject_set_name(&kset_p.kobj,"kset_p");  
  54.         kset_p.uevent_ops = &uevent_ops;  
  55.         kset_register(&kset_p);  
  56.    
  57.         kobject_set_name(&kset_c.kobj,"kset_c");  
  58.         kset_c.kobj.kset = &kset_p;  
  59.         kset_register(&kset_c);  
  60.         return 0;  
  61. }  
  62.    
  63. int kset_test_exit()  
  64. {  
  65.         printk("kset test exit./n");  
  66.         kset_unregister(&kset_p);  
  67.         kset_unregister(&kset_c);  
  68.         return 0;  
  69. }  
  70.    
  71. module_init(kset_test_init);  
  72. module_exit(kset_test_exit);  

测试效果:

[c-sharp]  view plain copy
  1. root@hacker:/home/hacker/kobject# insmod kset.ko  
  2. root@hacker:/home/hacker/kobject# dmesg  
  3. [  866.344079] kset test init.  
  4. [  866.344090] Filter: kobj kset_c.  
  5. [  866.344092] Name: kobj kset_c.  
  6. [  866.344097] uevent: kobj kset_c.  
  7. [  866.344099] ACTION=add.  
  8. [  866.344101] DEVPATH=/kset_p/kset_c.  
  9. [  866.344103] SUBSYSTEM=kset_name.  
  10.   
  11. root@hacker:/home/hacker/kobject# rmmod kset  
  12. root@hacker:/home/hacker/kobject# dmesg   
  13. [  892.202071] kset test exit.  
  14. [  892.202075] Filter: kobj kset_c.  
  15. [  892.202077] Name: kobj kset_c.  
  16. [  892.202083] uevent: kobj kset_c.  
  17. [  892.202085] ACTION=remove.  
  18. [  892.202087] DEVPATH=/kset_p/kset_c.  
  19. [  892.202089] SUBSYSTEM=kset_name.  

如果将kset_c.kobj.kset = &kset_p;这行注释掉,也就是不产生热插拔事件,效果如下:

[c-sharp]  view plain copy
  1. root@hacker:/home/hacker/kobject# insmod kset.ko  
  2. root@hacker:/home/hacker/kobject# dmesg  
  3. [   94.146759] kset test init.  

无论什么时候,当内核要为指定的kobject产生事件时,都要调用filter函数。如果filter返回0,将不产生事件,这里将返回值改为0,看效果:

[c-sharp]  view plain copy
  1. root@hacker:/home/hacker/kobject# insmod kset.ko  
  2. root@hacker:/home/hacker/kobject# dmesg  
  3. [  944.457502] kset test init.  
  4. [  944.457535] Filter: kobj kset_c.  
  5.   
  6. root@hacker:/home/hacker/kobject# rmmod kset  
  7. root@hacker:/home/hacker/kobject# dmesg  
  8. [  962.514146] kset test exit.  

下边是kobject的测试效果:

[c-sharp]  view plain copy
  1. root@hacker:/home/hacker/kobject# insmod kobject.ko  
  2. root@hacker:/home/hacker/kobject# dmesg  
  3. [ 1022.694855] kboject test init.  
  4.   
  5. root@hacker:/home/hacker/kobject# rmmod kobject  
  6. root@hacker:/home/hacker/kobject# dmesg  
  7. [ 1056.650200] kobject test exit.  
  8.   
  9. root@hacker:/sys/kobject_test# ls  
  10. kobj_config  
  11. root@hacker:/sys/kobject_test# cat kobj_config   
  12. root@hacker:/sys/kobject_test# dmesg  
  13. [ 1280.545220] have show.  
  14. [ 1280.545226] attrname:kobj_config.  
  15. root@hacker:/sys/kobject_test# echo "aha" > kobj_config   
  16. root@hacker:/sys/kobject_test# dmesg  
  17. [ 1280.545220] have show.  
  18. [ 1280.545226] attrname:kobj_config.  
  19. [ 1295.622228] havestore  
  20. [ 1295.622235] write: aha  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值