Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1700458
  • 博文数量: 174
  • 博客积分: 5493
  • 博客等级: 上校
  • 技术积分: 5802
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-05 15:13
个人简介

炼狱,是为追逐光芒

文章分类

全部博文(174)

文章存档

2017年(1)

2016年(3)

2015年(9)

2014年(5)

2013年(23)

2012年(56)

2011年(45)

2010年(32)

分类: LINUX

2011-12-27 11:36:45

  1. 此文写作过程参考了几个大牛的博客,他们分别是
  2. Tekkaman:http://blog.chinaunix.net/space.php?uid=20543672&do=blog&id=94312
  3. 《Linux那些事儿 之 我是Sysfs》:http://blog.csdn.net/fudan_abc/article/details/1768277
  4. http://hi.baidu.com/csdeny/blog/item/5a4f14f342d30dca0a46e038.html
  5. 还有国嵌的实验代码,对我理解kobject帮助很大,一并表示感谢

设备模型这一块相对来说比较复杂,不管是LDD3还是宋宝华的书,讲的都很抽象,我以前也看了好几遍,都是云里雾里,没有理出个头绪(是偶智商低么?)。ldd3认为设备模型这一块属于高级教材,对于多数程序来说是不必要的,但我实际工作中发现,虽然设备模型内核已经处理的很好,几乎不需要改动,但正确理解其工作机理,对认知Linux驱动,几乎可以说是个质的提升。so,还是静下心来,吧这个部分好好梳理一下,采用的方法是自下而上,先弄清楚最基本的概念,(以前方法相反,效果很杯催...)。

Linux 2.6内核的一个重要特色是提供了统一的内核设备模型。随着技术的不断进步,系统的拓扑结构越来越复杂,对智能电源管理、热插拔以及plug and play的支持要求也越来越高,2.4内核已经难以满足这些需求。为适应这种形势的需要,2.6内核开发了全新的设备模型。

1.sysfs
"sysfs is a ram-based filesystem initially based on ramfs. It provides a means
to export kernel data structures, their attributes, and the linkages between them to
userspace.” 
--- documentation/filesystems/sysfs.txt
Sysfs文件系统是一个类似于proc文件系统的特殊文件系统,用于把连接到系统上的设备和总线组织成分级的文件结构,向用户模式程序提供详细的内核数据结构信息。
  1. # ls /sys
  2. block class firmware vendor
  3. bus devices kernel module
  4. #
block: 包含所有的快设备,系统中的每个块设备在该目录下对于一个子目录,每个子目录包含一些属性文件,loop块设备使用文件来模拟的。
devices: 包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构。
bus: 在内核中注册的每条总线在该目录下对应一个子目录,每个总线又包含两个子目录devices,drivers,devices目录包含整个系统中属于该总线类型的设备,drivers目录包含所有属于该总线的驱动
drivers: 包含内核中已经注册的所有设备驱动
class: 系统中的设备类型,按功能划分。
kernel:内核中的配置参数
module:系统中的所有模块信息
sysfs这样被挂载的。
mount -t sysfs sysfs /sys
sysfs是一个特殊文件系统,并没有一个实际存放文件的介质。断电就消失了。简而言之,sysfs的信息来源是kobject层次结构,读一个sysfs文件,就是动态的从kobject结构提取信息,生成文件。
所以,首先要研究下sysfs文件系统的信息来源 -- kobject层次结构。kobject层次结构就是linux的设备模型。

2.kobject
kobject实现了基本的面向对象管理机制,是构成Linux2.6设备模型的核心结构,它与sysfs文件系统紧密相连,在内核中注册的每个kobject对象对应sysfs文件系统中的一个目录。kobject可以理解为类似JAVA,C++中基类,常被嵌入到其他类型(容器)中,比如bus,devices,drivers都是典型的容器,他们继承kobject基本功能,并扩展自身其他功能,这些容器通过通过kobject链接起来,形成树状结构。C语言中没有继承特性,通过结构体包含来实现类继承。
①kobject定义
  1. struct kobject {
  2.     const char * k_name;/*kobject 的名字数组(sysfs 入口使用的名字)指针;如果名字数组大小小于KOBJ_NAME_LEN,它指向本数组的name,否则指向另外分配的一个名字数组空间 */
  3.     char name[KOBJ_NAME_LEN];/*kobject 的名字数组,若名字数组大小不小于KOBJ_NAME_LEN,只储存前KOBJ_NAME_LEN个字符*/
  4.     struct kref kref;/*kobject 的引用计数*/
  5.     struct list_head entry;/*kobject 之间的双向链表,与所属的kset形成环形链表*/
  6.     struct kobject * parent;/*在sysfs分层结构中定位对象,指向上一级kset中的struct kobject kobj*/
  7.     struct kset * kset;/*指向所属的kset*/
  8.     struct kobj_type * ktype;/*负责对该kobject类型进行跟踪的struct kobj_type的指针*/
  9.     struct dentry * dentry;/*sysfs文件系统中与该对象对应的文件节点路径指针*/
  10.     wait_queue_head_t poll;/*等待队列头*/
  11. };
kobject 是组成设备模型的基本结构,初始它只被作为一个简单的引用计数, 但随时间的推移,其任务越来越多。现在kobject 所处理的任务和支持代码包括:
对象的引用计数 :跟踪对象生命周期的一种方法是使用引用计数。当没有内核代码持有该对象的引用时, 该对象将结束自己的有效生命期并可被删除。
sysfs 表述:在 sysfs 中出现的每个对象都对应一个 kobject, 它和内核交互来创建它的可见表述。
数据结构关联:整体来看, 设备模型是一个极端复杂的数据结构,通过其间的大量链接而构成一个多层次的体系结构。kobject 实现了该结构并将其聚合在一起。
热插拔事件处理 :kobject 子系统将产生的热插拔事件通知用户空间。
一个kobject对自身并不感兴趣,它存在的意义在于把高级对象连接到设备模型上。因此内核代码很少(甚至不知道)创建一个单独的 kobject;而kobject 被用来控制对大型域(domain)相关对象的访问,所以kobject 被嵌入到其他结构中。kobject 可被看作一个最顶层的基类,其他类都它的派生产物。 kobject 实现了一系列方法,对自身并没有特殊作用,而对其他对象却非常有效。
 对于给定的kobject指针,可使用container_of宏得到包含它的结构体的指针。

②kobject方法
  1. void kobject_init(struct kobject *kobj, struct kobj_type *ktype)/*初始化kobject结构*/

  2. int kobject_add(struct kobject *kobj, struct kobject *parent,const char *fmt, ...)//将kobject对象注册到Linux系统

  3. int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,struct kobject *parent, const char *fmt, ...)
  4. /*初始化kobject,并将其注册到Linux系统,手头实验平台是2.6.10的代码,注册用kobject_register(),基本过程是一样的*/

  5. int kobject_set_name(struct kobject *kobj, const char *fmt, ...)//设置kobject目录名字

  6. void kobject_del(struct kobject *kobj)/*从Linux系统中删除kobj对象*/

  7. struct kobject *kobject_get(struct kobject *kobj)
  8. //将kobject对象的引用计数加1,同时返回该对象指针

  9. void kobject_put(struct kobject *kobj)
  10. //将kobject对象的引用计数减1,如果引用计数为0,则调用release方法释放该kobject对象。

③kobj_type
kobject的ktype成员是一个指向kobj_type结构的指针,该结构记录了kobject的一些属性
  1. struct kobj_type {
  2.     void (*release)(struct kobject *kobj);
  3.     struct sysfs_ops *sysfs_ops;
  4.     struct attribute **default_attrs;
  5. };
release:用于释放kobject占用的资源,当kobject的应用计数为0时被调用。

  1. struct attribute {
  2.     const char *name;
  3.     struct module *owner;
  4.     mode_t mode;
  5. };
struct attribute:对应于kobject下的一个文件,name就是文件名。
  1. struct sysfs_ops {
  2.     ssize_t    (*show)(struct kobject *, struct attribute *,char *buf);
  3.     ssize_t    (*store)(struct kobject *,struct attribute *,const char *buf, size_t);
  4. };
show: 当用户读属性文件时,被调用,该函数将属性值写入buf返回给用户态。
store:当用户写属性文件时,被调用,用户存储用户传入的属性值。
加载一个kobject过程:
分配kobject内存空间,并清零(必须清零,否则出错,我板子上出现段错误)
设置kobject目录名字,ktype等成员
初始化,加载kobject
测试代码: kobject.rar  
实验现象:

kobject可以简单通俗的理解为一个对象,功能是用来在sysfs文件系统中管理一个目录,以及目录下文件属性。Linux内核模型中的几个典型模块bus,device,driver都是通过继承kobject对象(结构体包含),来组织sysfs文件系统的层级目录树状图。kobject是设备模型中最核心的数据结构,真正理解这个对象机制,才能掌握好后面的总线模型。

3.kset
kset是具有相同类型的object的集合,在sysfs中体现为一个目录,
  1. struct kset {
  2.     struct kobj_type * ktype; /*指向该kset对象类型的指针*/
  3.     struct list_head list;/*用于连接该kset中所有kobject以形成环形链表的链表头*/
  4.     spinlock_t list_lock;/*用于避免竞态的自旋锁*/
  5.     struct kobject kobj; /*嵌入的kobject*/
  6.     struct kset_uevent_ops * uevent_ops;
  7. /*处理热插拔时间的集合*/
  8. };
kset方法,与kobject类似
  1. void kset_init(struct kset *kset);
  2. int kset_add(struct kset *kset);
  3. int kset_register(struct kset *kset);
  4. void kset_unregister(struct kset *kset);

  5. /*管理 ksets 的引用计数:*/
  6. struct kset *kset_get(struct kset *kset);
  7. void kset_put(struct kset *kset);

  8. /* kset 也有一个名字,存储于嵌入的 kobject,因此设置它的名字用:*/
  9. kobject_set_name(&my_set->kobj, "The name");
热插拔事件
一个热插拔事件是一个从内核空间发送到用户空间的通知, 表明系统配置已经改变. 无论 kobject 被创建或删除,都会产生这种事件。热插拔事件会导致对 /sbin/hotplug 的调用, 它通过加载驱动程序, 创建设备节点, 挂载分区或其他正确动作响应事件。
热插拔事件的实际控制是通过一套存储于 kset_uevent_ops (《LDD3》中介绍的struct kset_hotplug_ops * hotplug_ops;在2.6.22.2中已经被kset_uevent_ops 结构体替换)结构的方法完成:

struct kset_uevent_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, char **envp,
            int num_envp, char *buffer, int buffer_size);
};


可以在 kset 结构的uevent_ops 成员中找到指向kset_uevent_ops结构的指针。

若在 kobject 中不包含指定的 kset , 内核将通过 parent 指针在分层结构中进行搜索,直到发现一个包含有kset的 kobject ; 接着使用这个 kset 的热插拔操作。当kset管理的kobject和kset发生变化时,这三个方法被调用。

三个方法功能如下:
(1) filter 函数让 kset 代码决定是否将事件传递给用户空间。如果 filter 返回 0,将不产生事件。以磁盘的 filter 函数为例,它只允许kobject产生磁盘和分区的事件,源码如下:

static int block_hotplug_filter(struct kset *kset, struct kobject *kobj)
{
 struct kobj_type *ktype = get_ktype(kobj);
    return ((ktype == &ktype_block) || (ktype == &ktype_part));
}

(2) 当调用用户空间的热插拔程序时,相关子系统的名字将作为唯一的参数传递给它。name 函数负责返回合适的字符串传递给用户空间的热插拔程序。

(3)热插拔脚本想得到的任何其他参数都通过环境变量传递。uevent 函数的作用是在调用热插拔脚本之前将参数添加到环境变量中。函数原型:

int (*uevent)(struct kset *kset, struct kobject *kobj, /*产生事件的目标对象*/
 char **envp,/*一个保存其他环境变量定义(通常为 NAME=value 的格式)的数组*/
 int num_envp, /*环境变量数组中包含的变量个数(数组大小)*/
 char *buffer, int buffer_size/*环境变量被编码后放入的缓冲区的指针和字节数(大小)*/
/*若需要添加任何环境变量到 envp, 必须在最后的添加项后加一个 NULL 入口,使内核知道数组的结尾*/
        );
/*返回值正常应当是 0,若返回非零值将终止热插拔事件的产生*/

热插拔事件的产生通常是由在总线驱动程序层的逻辑所控制。
一个简单的测试代码:
  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. int kset_filter(struct kset *kset, struct kobject *kobj)
  16. {
  17.         printk("Filter: kobj %s.\n",kobj->name);
  18.         return 1;
  19. }
  20.  
  21. const char *kset_name(struct kset *kset, struct kobject *kobj)
  22. {
  23.         static char buf[20];
  24.         printk("Name: kobj %s.\n",kobj->name);
  25.         sprintf(buf,"%s","kset_name");
  26.         return buf;
  27. }
  28.  
  29. int kset_uevent(struct kset *kset, struct kobject *kobj, char **envp,
  30.             int num_envp, char *buffer, int buffer_size)
  31. {
  32.         //int i = 0;
  33.         printk("uevent: kobj %s.\n",kobj->name);
  34. #if 0
  35.         while( i < env->envp_idx){
  36.                 printk("%s.\n",env->envp[i]);
  37.                 i++;
  38.         }
  39. #endif
  40.         return 0;
  41. }

  42. struct kset_uevent_ops uevent_ops =
  43. {
  44.         .filter = kset_filter,
  45.         .name = kset_name,
  46.         .uevent = kset_uevent,
  47. };
  48.  
  49. int kset_test_init()
  50. {
  51.         printk("kset test init.\n");
  52.         kset_p = kzalloc(sizeof(struct kset), GFP_KERNEL);
  53.         kset_c = kzalloc(sizeof(struct kset), GFP_KERNEL);
  54.         
  55.         kobject_set_name(&kset_p->kobj,"kset_p");
  56.         kset_p->uevent_ops = &uevent_ops;
  57.         kset_register(kset_p);
  58.  
  59.         kobject_set_name(&kset_c->kobj,"kset_c");
  60.         kset_c->kobj.kset = kset_p;
  61.         kset_register(kset_c);
  62.         return 0;
  63. }
  64.  
  65. int kset_test_exit()
  66. {
  67.         printk("kset test exit.\n");
  68.         kset_unregister(&kset_p);
  69.         kset_unregister(&kset_c);

  70.         kfree(kset_p);
  71.         kfree(kset_c);
  72.         return 0;
  73. }
  74.  
  75. module_init(kset_test_init);
  76. module_exit(kset_test_exit);
4.子系统
子系统是对整个内核中一些高级部分的表述。子系统通常(但不一定)出现在 sysfs分层结构中的顶层,内核子系统包括 block_subsys(/sys/block 块设备)、 devices_subsys(/sys/devices 核心设备层)以及内核已知的用于各种总线的特定子系统。
每个 kset 必须属于一个子系统,子系统成员帮助内核在分层结构中定位 kset 。由于子系统在新版内核中已经被去掉用kset代替了。,就不详细研究了。
阅读(3119) | 评论(0) | 转发(6) |
给主人留下些什么吧!~~