Linux设备驱动工程师之路——设备模型(上)底层模型

Linux设备驱动工程师之路——设备模型(上)底层模型

K-Style

转载请注明来自于衡阳师范学院08电2  Y-Kee http://blog.csdn.net/ayangke,QQ:843308498

 

一、重要知识点

 

         1.Sysfs文件系统

       Sysfs文件系统是一种类似于proc文件系统的特殊文件系统,它存在于内存当中,当系统启动时由内核挂载于内存当中。用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的数据结构信息。

 

    2.Linux设备底层模型

 

       1)为什么要使用设备模型

       随着系统的拓扑结构越来越复杂,以及要支持诸如电源管理等新特性的要求,于是在2.6的内核中出现了设备模型。设备模型其实就是一套数据结构建立起来的模型。内核使用该模型支持了多种不同的任务,包括:

       a.电源管理和系统关机

       设备模型使操作系统能够以正确的顺序遍历系统硬件。

       b.与用户空间通信

       Sysfs文件系统向用户空间提供系统信息以及改变操作参数的结构。

       c.热插拔事件

       d.设备类型

       系统中许多部分对设备如何连接不感兴趣,但是他们需要知道哪些类型设备时可用的。设备模型提供了将设备分类的机制。

       e.对象的生命周期

       上述的许多功能,包括热插拔支持和sysfs,使得内核中管理对象的工作更为复杂。设备模型需要创造一套机制管理对象的生命周期。

 

       2)Kobject

       如果说设备模型是一套房子的话,Kobject就是构造房子的砖块。每个注册的Kobject的都对应与Sysfs文件系统中的一个目录。Kobject是组成设备模型的基本结构。类似于C++的基类,它潜入于更大的对象中——所谓的容器,用来描述设备模型的组件。如bus,device,drivers都是典型的容器。这些容器就是通过kobject连接起来,形成一个树状结构。这个树状结构就与/sys文件系统对应。不过kobject只能建立单层结构,也就是只能建立一级目录,要建立多级目录,还要使用后面要介绍的Kset。

Kobject结构定义为:

struct kobject {

char * k name; 指向设备名称的指针

char name[KOBJ NAME LEN]; 设备名称

struct kref kref; 对象引用计数

struct list head entry; 挂接到所在kset中去的单元

struct kobject * parent; 指向父对象的指针

struct kset * kset; 所属kset的指针

struct kobj type * ktype; 指向其对象类型描述符的指针

struct dentry * dentry; sysfs文件系统中与该对象对应的文件节点路径指针

};

相关操作函数:

void kobjet_init(struct kobject*kobj)

初始化Kobject

int kobject_add(struct kobject*kobj)

将Kobject对象注册到linux系统,如果失败则返回一个错误码.

int kobject_init_and_add(structkobject *kobj, kobj_type *ktype, struct kobject *parent, const *fmt…)

初始化并注册kobject,kobject传入要初始化的Kobject对象,ktype将在后面介绍到,parent指向上级的kobject对象,如果指定位NULL,将在/sys的顶层创建一个目录。*fmt为kobject对象的名字。

kobject的ktype对象是一个指向kobject_type结构的指针,该结构记录了kobject对象的一些属性。每个kobject都需要对应一个相应的kobject结构。

struct kobj_type{

       void (*release)(struct kobject *kobj);

       structsysfs_ops *sysfs_ops;

       structattribute **default_attrs;

};

release方法用于释放kobject占用的资源,当kobject引用计数为0时被调用。

kobje_type的attribute成员:

struct attribute{

              char*name;//属性文件名

       structmodule *owner;

       mode_tmode;

}

struct attribute(属性):对应于kobject的目录下一个文件,name就是文件名。

kobje_type的struct sysfs_ops成员:

struct sysfs_ops

{

ssize_t (*show)(structkobejct *,  struct attribute *,  char  *name);

ssize_t (*store)(structkobejct *,  struct attribute *,  char  *name);

}

       show:当用户读属性文件时,该函数被调用,该函数将属性值存入buffer中返回给用户态;

       store:当用户写属性文件时,该函数被调用,用于存储用户存入的属性值。

Kobject测试模块:

  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.         sprintf(buf,"%s\n",attr->name);  
  50.         return strlen(attr->name)+2;  
  51. }  
  52.    
  53. ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count)  
  54. {  
  55.         printk("havestore\n");  
  56.         printk("write: %s\n",buf);  
  57.         return count;  
  58. }  
  59.    
  60. struct kobject kobj;  
  61. static int kobj_test_init()  
  62. {  
  63.         printk("kboject test init.\n");  
  64.         kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");  
  65.         return 0;  
  66. }  
  67.    
  68. static int kobj_test_exit()  
  69. {  
  70.         printk("kobject test exit.\n");  
  71.         kobject_del(&kobj);  
  72.         return 0;  
  73. }  
  74.    
  75. module_init(kobj_test_init);  
  76. module_exit(kobj_test_exit);  

测试结果:



在/sys目录下创建了kobject_test目录

在kobject_test目录下有kobj_config文件

读kobject_config文件则调用了show函数。并在用户空间显示了show返回的kobject对象名字。

写kobject_config文件调用了store函数。

 

3)Kset

kset的主要功能是包容;我们可以认为他他是kobject的顶层容器。实际上,在每个kset对象的内部,包含了自己的kobject,并且可以用多种处理kobject的方法处理kset。如果说kobject是基类的话,那么kset就是派送类。kobject通过kset组织成层次化的结构,kset是相同类型的组合。通俗的讲,kobject建立一级的子目录,kset可以为kobject建立多级的层次性的父目录。

struct kset {

struct subsystem * subsys; 所在的subsystem的指针

struct kobj type * ktype; 指向该kset对象类型描述符的指针

struct list head list; 用于连接该kset中所有kobject的链表头

struct kobject kobj; 嵌入的kobject

struct  kset_uevent_ops * uevent_ops; 指向热插拔操作表的指针

};

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

kset与kobject的关系图


 

Kset操作:

int kset_register(struct kset*kset)

注册kset

void kset_unregister(struct kset*kset)

注销kset

       热插拔事件:在linux系统中,当系统配置发生变化时,如添加kset到系统或移动kobject,一个通知会从内核空间发送到用户空间,这就是热插拔事件。热插拔事件会导致用户空间中的处理程序(如udev,mdev)被调用,这些处理程序会通过加载驱动程序,创建设备节点等来响应热插拔事件。

       对热插拔事件的实际控制是由struct kset_uevent_ops结构中的函数完成的。

       struct kset_uevnt_ops{

       int (*filter)(struct kset *kset,struct  kobject *kobj);

       const char *(*name)(struct kset *kset, struct kobject *kobj );

       int (*uevent)(struct kset *kset,struct  kobject *kobj,struct kobj_uevent *env);

}

filter决定是否产生事件,如果返回0,将不产生事件。

name向用户空间传递一个合适的字符串

uevent通过环境变量传递任何热插拔脚本需要的信息,他会在(udev或mdev)调用之前,提供添加环境变量的机会。

 

       kset测试模块:

  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);  

测试结果:



可以看出当kset加载时,在/sys下创建了一个kset_p,在kset_p下面创建了kset_c,当kset模块被加载和卸载时都产生了热插拔事件。

 

参考书籍:

       《linux那些事儿之我是sysfs》

       《linux设备驱动程序(第三版)》

       《国嵌课件》

 

PS:

请关注下一篇《Linux设备驱动工程师之路——设备模型(下)上层模型》

写这玩意儿把我累坏了,呵呵。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值