前几天回家转了转,跟爸妈聊到深夜,嗬嗬,最后爸妈推荐了一部电视剧《青春期撞上更年期》,所以回来以后,电视剧就开看啦,不过我觉得这不算是浪费时间吧,好的电视剧能给人不少感悟、感动和成长。
下面开始继续我的笔记···
上次写到struct cdev,这次接着从struct kobject开始记录:
Linux 2.6引入新的设备管理机制kobject,通过这个数据结构使所有的设备在底层都具有统一的接口,kobject提供基本的对象管理,是构成Linux2.6设备模型的核心结构,它与sysfs文件系统紧密关联,每个在内核中注册的kobject对象都对应与sysfs文件系统的一个目录。多花一些时间来研究kobject是十分有意义的工作。下面我仔细研究了一下:
Kobject通常通过kset组织成层次化的结构,kset是具有相同类型的kobject的集合。在Linux 2.6.30内核
linux/include/linux/kobject.h中查看struct kobject结构体的实现如下:
1: struct kobject {
2: const char *name; //名称
3: struct list_head entry;
4: struct kobject *parent;//父指针
5: struct kset *kset;//kobject对象的集合体
6: struct kobj_type *ktype; //kobject对象被关联到的一种特殊的类型
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: };
Kobject 是基础的结构, 它保持设备模型在一起. 初始地它被作为一个简单的引用计数, 但是它的责任已随时间增长, 并且因此有了它自己的战场. struct kobject 所处理的任务和它的支持代码现在包括:
-
对象的引用计数
-
常常, 当一个内核对象被创建, 没有方法知道它会存在多长时间. 一种跟踪这种对象生命周期的方法是通过引用计数. 当没有内核代码持有对给定对象的引用, 那个对象已经完成了它的有用寿命并且可以被删除.
- sysfs 表示
-
-
在 sysfs 中出现的每个对象在它的下面都有一个 kobject, 它和内核交互来创建它的可见表示.
- 数据结构粘和
-
-
设备模型是, 整体来看, 一个极端复杂的由多级组成的数据结构, 各级之间有许多连接. kobject 实现这个结构并且保持它在一起.
- 热插拔事件处理
-
-
kobject 子系统处理事件的产生, 事件通知用户空间关于系统中硬件的来去.
你可能从前面的列表总结出 kobject 是一个复杂的结构. 这可能是对的. 通过一次看一部分, 但是, 是有可能理解这个结构和它如何工作的. 摘自 ——《Linux设备驱动程序(第三版)》361页
在我们进入细节前, 值得花些时间理解如何使用 kobjects. 而且过程中会发现很多有意思的东西。如果你看被 kobjects 处理的函数列表, 你会看到它们都是代表其他对象进行的服务. 一个 kobject, 换句话说, 对其自己很少感兴趣; 它存在仅仅为了结合一个高级对象到设备模型。
因此, 对于内核代码它很少(甚至不知道)创建一个孤立的 kobject; 相反, kobject 被用来控制存取更大的, 特定域的对象.。
为此, kobject 被嵌入到其他结构中. 如果你习惯以面向对象的术语考虑事情, kobject 可被看作一个顶级的, 抽象类, 其他的类自它而来. 一个 kobject 实现一系列功能, 这些功能对自己不是特别有用而对其他对象是好的. C 语言不允许直接表达继承, 因此其他的技术 -- 例如将一个结构嵌入另一个。(看到这大家可能有点糊涂,怀疑C语言真的有这么厉害么?其实C语言真的很厉害,以前我看过一本《C算法》(第一卷)的书,很棒,里面有很多实例都是用C语言实现面向对象编程。)
在上一篇记录中有一个十分关键的结构体:
1: struct cdev {
2: struct kobject kobj;
3: struct module *owner; //所属模块
4: const struct file_operations *ops;
5: //文件操作结构,在写驱动时,其结构体内的大部分函数要被实现
6: struct list_head list;
7: dev_t dev;//设备号,int 类型,高12位为主设备号,低20位为次设备号
8: unsigned int count;
9: };
从中可以看到,里面嵌入了kobject结构体,如果需要使用该结构体,只需要访问kobject成员就能获得嵌入的kobject对象。但是我们在使用kobject代码时经常遇到相反的问题:对于给定的一个kobject指针,如何获取包含它的结构体指针呢?必然要抛弃一些比较简单的想法(比如你假设kobject处于包含其结构体的开始位置),此时怎么办呢?Linux黑客们帮我们写了一个十分给力的宏,也许学C语言的时候,你还没发现宏的好处,这里我们研究一下这个有意思的宏:container_of宏。
在linux内核中经常用到container_of,它被定义在:
linux/include/linux/kernel.h中,原型为:
1. /**
2. * container_of - cast a member of a structure out to the containing structure
3. * @ptr: the pointer to the member.
4. * @type: the type of the container struct this is embedded in.
5. * @member: the name of the member within the struct.
6. *
7. */
8. #define container_of(ptr, type, member) ({ /
9. const typeof( ((type *)0)->member ) *__mptr = (ptr); /
10. (type *)( (char *)__mptr - offsetof(type,member) );})
type
|-----------------|
| |
| |
|-----------------|
ptr-->| member ---------|
|-----------------|
| |
| |
其中offsetof宏定义在[include/linux/stddef.h]中定义为:
1: 1. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
这里,要说明的是,((size_t) &((TYPE *)0)->MEMBER)把0地址转化为TYPE结构的指针, 然后获取该结构中MEMBER成员的指针,并将其强制转换为size_t类型。于是,由于结构从0地址开始定义,因此,这样求出的MEMBER成员地址,实际上就是它在结构中的偏移量。
分析可知__mptr指向的是一个type结构里typeof(((type *)0)->member)类型member成员的指针,offsetof(type,member)是这个成员在结构中的偏移,单位是字节,所以为了计算type结构的起始地址,__mptr减去它自己的偏移。
至此应该明白这个宏的意思了,它十分关键,以后将常常使用。以后要讲的open函数中就将使用这个宏。
下面看下struct ktype结构体:
1: struct kobj_type {
2: void (*release)(struct kobject *kobj); //kobject引用计数减至0时要调用的析构函数
3: struct sysfs_ops *sysfs_ops; //描述了sysfs文件读写时的特性
4: struct attribute **default_attrs; //定义了该kobject相关的默认属性
5: };
而struct kset 的实现如下:
1: struct kset {
2: struct list_head list; //连接该集合(kset)中所有kobject对象
3: spinlock_t list_lock;
4: struct kobject kobj; //该集合的基类
5: struct kset_uevent_ops *uevent_ops;//uevent相关操作
6: };
看完主要的kobject的相关结构体,下面接着看kobject的常用的管理与操作:
void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
该函数用来初始化kobject,初始化并设置名称后,还需要为它设置kset和ktype字段。
kobject的引用计数:
主要有两个函数:
struct kobject *kobject_get(struct kobject *kobj); //增加一个引用计数
void kobject_put(struct kobject *kobj); //减少引用计数
回宿舍了 此篇明天接着写·····
最后插入一段有意思的东西,大家应该写程序都写很久了,下面我说一个很简单的问题,这个问题就是写文章的时候,教研室的同学问的:
一个char类型的变量A,现在存值为255,用double类型强制转换以后A的值为多少?为什么?
就是提醒大家一下,学多了,也别忘了最基础的知识!