设备模型这一块相对来说比较复杂,不管是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文件系统的特殊文件系统,用于把连接到系统上的设备和总线组织成分级的文件结构,向用户模式程序提供详细的内核数据结构信息。
- # ls /sys
-
block class firmware vendor
-
bus devices kernel module
-
#
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定义在 。
- struct kobject {
-
const char * k_name;/*kobject 的名字数组(sysfs 入口使用的名字)指针;如果名字数组大小小于KOBJ_NAME_LEN,它指向本数组的name,否则指向另外分配的一个名字数组空间 */
-
char name[KOBJ_NAME_LEN];/*kobject 的名字数组,若名字数组大小不小于KOBJ_NAME_LEN,只储存前KOBJ_NAME_LEN个字符*/
-
struct kref kref;/*kobject 的引用计数*/
-
struct list_head entry;/*kobject 之间的双向链表,与所属的kset形成环形链表*/
-
struct kobject * parent;/*在sysfs分层结构中定位对象,指向上一级kset中的struct kobject kobj*/
-
struct kset * kset;/*指向所属的kset*/
-
struct kobj_type * ktype;/*负责对该kobject类型进行跟踪的struct kobj_type的指针*/
-
struct dentry * dentry;/*sysfs文件系统中与该对象对应的文件节点路径指针*/
-
wait_queue_head_t poll;/*等待队列头*/
-
};
kobject 是组成设备模型的基本结构,初始它只被作为一个简单的引用计数, 但随时间的推移,其任务越来越多。现在kobject 所处理的任务和支持代码包括:
对象的引用计数 :跟踪对象生命周期的一种方法是使用引用计数。当没有内核代码持有该对象的引用时, 该对象将结束自己的有效生命期并可被删除。
sysfs 表述:在 sysfs 中出现的每个对象都对应一个 kobject, 它和内核交互来创建它的可见表述。
数据结构关联:整体来看, 设备模型是一个极端复杂的数据结构,通过其间的大量链接而构成一个多层次的体系结构。kobject 实现了该结构并将其聚合在一起。
热插拔事件处理 :kobject 子系统将产生的热插拔事件通知用户空间。
一个kobject对自身并不感兴趣,它存在的意义在于把高级对象连接到设备模型上。因此内核代码很少(甚至不知道)创建一个单独的 kobject;而kobject 被用来控制对大型域(domain)相关对象的访问,所以kobject 被嵌入到其他结构中。kobject 可被看作一个最顶层的基类,其他类都它的派生产物。 kobject 实现了一系列方法,对自身并没有特殊作用,而对其他对象却非常有效。
对于给定的kobject指针,可使用container_of宏得到包含它的结构体的指针。
②kobject方法
- void kobject_init(struct kobject *kobj, struct kobj_type *ktype)/*初始化kobject结构*/
-
int kobject_add(struct kobject *kobj, struct kobject *parent,const char *fmt, ...)//将kobject对象注册到Linux系统
-
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,struct kobject *parent, const char *fmt, ...)
-
/*初始化kobject,并将其注册到Linux系统,手头实验平台是2.6.10的代码,注册用kobject_register(),基本过程是一样的*/
-
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)//设置kobject目录名字
-
void kobject_del(struct kobject *kobj)/*从Linux系统中删除kobj对象*/
-
struct kobject *kobject_get(struct kobject *kobj)
-
//将kobject对象的引用计数加1,同时返回该对象指针
-
void kobject_put(struct kobject *kobj)
-
//将kobject对象的引用计数减1,如果引用计数为0,则调用release方法释放该kobject对象。
③kobj_type
kobject的ktype成员是一个指向kobj_type结构的指针,该结构记录了kobject的一些属性
- struct kobj_type {
-
void (*release)(struct kobject *kobj);
-
struct sysfs_ops *sysfs_ops;
-
struct attribute **default_attrs;
-
};
release:用于释放kobject占用的资源,当kobject的应用计数为0时被调用。
- struct attribute {
- const char *name;
- struct module *owner;
- mode_t mode;
-
};
struct attribute:对应于kobject下的一个文件,name就是文件名。
- struct sysfs_ops {
-
ssize_t (*show)(struct kobject *, struct attribute *,char *buf);
-
ssize_t (*store)(struct kobject *,struct attribute *,const char *buf, size_t);
-
};
show: 当用户读属性文件时,被调用,该函数将属性值写入buf返回给用户态。
store:当用户写属性文件时,被调用,用户存储用户传入的属性值。
加载一个kobject过程:
①分配kobject内存空间,并清零(必须清零,否则出错,我板子上出现段错误)
②设置kobject目录名字,ktype等成员
③初始化,加载kobject
实验现象:
kobject可以简单通俗的理解为一个对象,功能是用来在sysfs文件系统中管理一个目录,以及目录下文件属性。Linux内核模型中的几个典型模块bus,device,driver都是通过继承kobject对象(结构体包含),来组织sysfs文件系统的层级目录树状图。kobject是设备模型中最核心的数据结构,真正理解这个对象机制,才能掌握好后面的总线模型。
3.kset
kset是具有相同类型的object的集合,在sysfs中体现为一个目录,
- struct kset {
-
struct kobj_type * ktype; /*指向该kset对象类型的指针*/
-
struct list_head list;/*用于连接该kset中所有kobject以形成环形链表的链表头*/
-
spinlock_t list_lock;/*用于避免竞态的自旋锁*/
-
struct kobject kobj; /*嵌入的kobject*/
-
struct kset_uevent_ops * uevent_ops;
-
/*处理热插拔时间的集合*/
-
};
kset方法,与kobject类似
- void kset_init(struct kset *kset);
-
int kset_add(struct kset *kset);
-
int kset_register(struct kset *kset);
-
void kset_unregister(struct kset *kset);
-
-
/*管理 ksets 的引用计数:*/
-
struct kset *kset_get(struct kset *kset);
-
void kset_put(struct kset *kset);
-
-
/* kset 也有一个名字,存储于嵌入的 kobject,因此设置它的名字用:*/
-
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,若返回非零值将终止热插拔事件的产生*/ |
热插拔事件的产生通常是由在总线驱动程序层的逻辑所控制。
一个简单的测试代码:
- #include <linux/device.h>
-
#include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <linux/init.h>
-
#include <linux/string.h>
-
#include <linux/sysfs.h>
-
#include <linux/stat.h>
-
#include <linux/kobject.h>
-
-
MODULE_AUTHOR("David Xie");
-
MODULE_LICENSE("Dual BSD/GPL");
-
-
struct kset * kset_p;
-
struct kset * kset_c;
-
-
int kset_filter(struct kset *kset, struct kobject *kobj)
-
{
-
printk("Filter: kobj %s.\n",kobj->name);
-
return 1;
-
}
-
-
const char *kset_name(struct kset *kset, struct kobject *kobj)
-
{
-
static char buf[20];
-
printk("Name: kobj %s.\n",kobj->name);
-
sprintf(buf,"%s","kset_name");
-
return buf;
-
}
-
-
int kset_uevent(struct kset *kset, struct kobject *kobj, char **envp,
-
int num_envp, char *buffer, int buffer_size)
-
{
-
//int i = 0;
-
printk("uevent: kobj %s.\n",kobj->name);
-
#if 0
-
while( i < env->envp_idx){
-
printk("%s.\n",env->envp[i]);
-
i++;
-
}
-
#endif
-
return 0;
-
}
-
-
struct kset_uevent_ops uevent_ops =
-
{
-
.filter = kset_filter,
-
.name = kset_name,
-
.uevent = kset_uevent,
-
};
-
-
int kset_test_init()
-
{
-
printk("kset test init.\n");
-
kset_p = kzalloc(sizeof(struct kset), GFP_KERNEL);
-
kset_c = kzalloc(sizeof(struct kset), GFP_KERNEL);
-
-
kobject_set_name(&kset_p->kobj,"kset_p");
-
kset_p->uevent_ops = &uevent_ops;
-
kset_register(kset_p);
-
-
kobject_set_name(&kset_c->kobj,"kset_c");
-
kset_c->kobj.kset = kset_p;
-
kset_register(kset_c);
-
return 0;
-
}
-
-
int kset_test_exit()
-
{
-
printk("kset test exit.\n");
-
kset_unregister(&kset_p);
-
kset_unregister(&kset_c);
-
-
kfree(kset_p);
-
kfree(kset_c);
-
return 0;
-
}
-
-
module_init(kset_test_init);
-
module_exit(kset_test_exit);
4.子系统
子系统是对整个内核中一些高级部分的表述。子系统通常(但不一定)出现在 sysfs分层结构中的顶层,内核子系统包括 block_subsys(/sys/block 块设备)、 devices_subsys(/sys/devices 核心设备层)以及内核已知的用于各种总线的特定子系统。
每个 kset 必须属于一个子系统,子系统成员帮助内核在分层结构中定位 kset 。由于子系统在新版内核中已经被去掉用kset代替了。,就不详细研究了。
阅读(2195) | 评论(0) | 转发(0) |