第十一章 内核的数据类型
坚持使用严格的数据类型,并且使用-Wall -Wstrict-prototypes选项编译可以防止大多数的代码缺陷
内核使用的数据类型主要分为三大类:
- 标准C语言类型,类似int
- 类似u32这样有确定大小的类型
- 类似pid_t这样用于特定内核对象的类型
使用标准C语言类型
在不同的体系架构上,普通C语言的数据类型所占空间的大小并不相同。
Linux系统中,指针和long整型的大小总是相同的。
为数据项分配确定的空间大小
有时内核代码需要特定大小的数据项,多半是用来匹配预定义的二进制结构或者和用户口空间进行通讯或者通过在结构体中插入"填白 padding"字段来对齐数据。
当需要知道自己的数据大小时,内核提供了下列数据类型,定义在<asm/types.h>
中
- u8 无符号字节8位
- u16 无符号字 16位
- u32 无符号32位
- u64 无符号64位
相应的有符号类型也存在,只需将名字中的u用s替换就可以了。
接口特定的类型
内核中最常用的数据类型由typedef
声明,这样可以防止出现任何移植性问题。
当需要打印一些接口特定的数据类型时,最行之有效的方法就是将其强制转换成可能的最大类型(通常是long
或者unsigned long
),然后用相应格式。
因为格式和类型相匹配,而且也不会丢失数据位,所以这种做法不会产生错误或者警告。
其他有关移植性的问题
- 通用原则:
避免使用显式的常量值,代码通过使用预处理的宏使之参数化。
- 时间间隔:
使用
jiffies
计算时间间隔的时候,应该用HZ(每秒定时器中断的次数)来衡量。 - 页大小:
内存页的大小为
PAGE_SIZE
字节。 - 字节序:
u32 cpu_to_le32(u32) u32 le32_to_cpu(u32)
这两个宏将一个CPU使用的值转换成一个无符号的32位小头数值,或者相反。不管CPU是大头还是小头,也不管CPU是否是一个32位处理器,这两个方法都能正常工作。 - 数据对齐:
为了编写可以在不同平台之间可移植的数据项的数据结构,应该始终强制数据项的自然对齐。
- 自然对齐(
natural alignment
)是指在数据项大小的整数倍(例如 8字节数据项存入8的整数倍的地址)的地址处存储数据项。
- 自然对齐(
- 链表:
内核建立了一套标准的循环、双向链表的实现,用于操作系统内核经常需要维护数据结构的列表。链表操作函数定义于<linux/list.h>,后续使用时再深入研究。
第十二章 PCI驱动程序
待补充
第十三章 USB驱动程序
待补充
第十四章 Linux设备模型
内核提供了统一的设备模型,并且使用该抽象模型支持了多种不同的任务,包括:
- 电源管理和系统关机
完成这些工作需要一些对系统结构的理解,比如一个USB宿主适配器,在处理完所有与其连接的设备前是不能被关闭的。设备模型使得操作系统能够以正确的顺序遍历系统硬件
- 与用户空间通信
sysfs虚拟文件系统的实现与设备模型密切相关,并且向外界展示了它所表述的结构。向用户空间提供所提供的系统信息,以及改变操作参数的接口,将越来越多地通过sysfs实现,也就是说,通过设备模型实现。
- 热插拔设备
越来越多的计算机设备可被动态的热插拔了,也就是说,外围设备可根据用户的需要安装与卸载。内核的热插拔机制可以处理热插拔设备,特别是能够与用户空间进行关于插拔设备的通信,而这种机制也是通过设备模型管理的。
- 设备类型
系统中的许多部分对设备如何连接的信息并不感兴趣,但是它们需要知道哪些类型的设备是可以使用的。设备模型包括了将设备分类的
机制,它会在更高的功能层上描述这些设备,并使得这些设备对用户空间可见。 - 对象生命周期
设备模型的实现需要创建一系列机制以处理对象的生命周期、对象之间的关系,以及这些对象在用户空间中的表示。
kobject、kset和子系统
kobject结构所能处理的任务以及它所支持的代码包括:
- 对象的引用计数
当一个内核对象被创建时,跟踪此对象生命周期的一个方法是使用引用计数,当内核中没有代码持有该对象的引用时,该对象将结束自己的有效生命周期,并且可以被删除。
- sysfs表述
在sysfs中显示的每一个对象,都对应一个kobject,它被用来与内核交互并创建它的可见表述
- 数据结构关联
从整体上看,设备模型是一个友好而复杂的数据结构,通过在其间的大量连接而构成一个多层次的体系结构,kobject实现了该结构并把它们聚合在一起。
- 热插拔事件处理
当系统中的硬件被热插拔时,在kobject子系统控制下,将产生事件以通知用户空间。
kobject基础知识
kobject
是一种数据结构,定于与<linux/kobject.h>
嵌入的kobject
内核代码很少去创建一个单独的kobject对象,相反,kobject用于控制对大型域相关对象的访问,kobject会被嵌入到其它结构中。在C语言汇总不允许直接描述继承关系,因此使用了诸如在一个结构中嵌入另一个结构的技术。
kobject的初始化
- 将整个
kobject
设置为0 - 调用
void kobject_init(struct kobject *kobj)
,这个方法设置kobject的引用计数为1 - 调用
int kobject_set_name(struct kobject *kobj, const char *format, ...)
设置kobject
的名字。
对引用计数的操作
struct kobject *kobject_get(struct kobject *kobj)
: 该函数将增加kobject的引用计数,并返回指向kobject的指针。
void kobject_put(struct kobject *kobj)
: 当引用被释放时调用kobject_put减少引用计数
kobject层次结构、kset和子系统
内核用kobject结构将各个对象连接起来组成一个分层的结构体系,从而与模型化的子系统相匹配,有两种独立的机制用于连接:parent指针和kset。
在kobject
结构的parent
成员中,保存了另一个kobject结构的指针,这个结构表示了分层结构中上一层的节点。
例如:一个kobject结构表示了一个USB设备,它的parent指针可能指向了表示USB集线器的对象,而USB设备是插在USB集线器上的。
parent指针最重要的用途是在sysfs
分层结构中定位对象。
kset
kset
是嵌入相同类型结构的kobject
集合,kset的主要功能是包容,可以认为它是kobject
的顶层容器类。
kset
总是在sysfs
中出现,一旦设置了kset
并把它添加到系统中,将在sysfs
中创建一个目录,kobject
不必在sysfs
中表示,但是kset
中的每一个kobject
成员都将在sysfs
中得到表述。
创建一个对象时,通常要把一个kobject
添加到kset
中去,主要过程包括:
- 把
kobject
的kset
成员指向目的kset - 调用
int kobject_add(struct kobject *kobj)
添加kobject
。
kset 在一个标准的内核链表中保存了它的子节点,在大多数情况下,所包含的kobject会在它们的parent成员中保存kset(严格地说是其内嵌的kobject)的指针.
子系统
子系统是对整个内核中的一些高级部分的表述。
子系统通常显示在sysfs
分层结构中的顶层。
内核中的子系统包括block_subsys
(对块设备来说是/sys/block
)、device_subsys
(/sys/devices
,设备分层结构的核心)以及内核所知晓的用于各种总线的特定子系统。
低层sysfs操作
kobject
是隐藏在sysfs
虚拟文件系统后的机制,对于sysfs
中的每个目录,内核中都会存在一个对应的kobject
,每一个kobject
都输出一个或者多个属性,它们在kobject
的sysfs
目录中表现为文件,其中的内容由内核生成。
<linux/sysfs.h>
中包含了sysfs
的工作代码,其中为了理解如何创建sysfs
的入口,需要了解如下知识:
-
kobject
在sysfs
中的入口始终是一个目录,因此对kobject_add
的调用将在sysfs
中创建一个目录,通常这个目录包含一个或者多个属性。 -
分配给kobject(使用kobject_set_name函数)的名字是sysfs中的目录名,这样处于sysfs分层结构相同的部分中的kobject必须有唯一的名字,该名字必须是合法的文件名:不能包含反斜杠,并且不要使用空格
-
sysfs
入口在目录中的位置对应于kobject
的parent
指针,如果调用kobject_add
的时候,parent
是NULL,它将被设置为嵌入到新kobject
的kset
中的kobject
,这样sysfs
分层结构通常与kset
创建的内部结构相匹配。如果parent
和kset
都是NULL,则会在最高层创建sysfs
目录
默认属性
当创建kobject的时候,都会给每个kobject一系列默认属性,这些属性保存在kojb_type结构中:
struct kobj_type {
void (*release)(struct kobject *);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
default_attrs
成员保存了属性列表,用于创建该类型的每一个kobject
,sysfs_ops
提供了实现这些属性的方法。
struct attribute {
char *name; //属性名字,在kobject的sysfs目录中显示
struct moudle *owner; //指向模块的指针,该模块负责实现这些属性
ode_t mode; //属性的保护位
};
kobj_type->sysfs_ops成员: 属性具体的实现
struct sysfs_ops {
ssize_t (*show)(struct kobject *kobj, struct attribute *attr, char *buffer);
ssize_t (*store)(struct kobject *kobj, struct attribute *attr, const char *buffer