50m 20p
第11 章 内核中的数据类型
标准C 类型的使用
尽量避免使用 int之类标准类型,因为它是平台相关的。即不同平台占用的字节数可能不同。
## 安排一个明确大小给数据项
内核提供了下列数据类型来使用。所有的数据声明在 <asm/types.h>, 它又被 <linux/types.h> 包含.
u8; /* unsigned byte (8bits) */
u16; /* unsigned word(16 bits) */
u32; /* unsigned 32-bitvalue */
u64; /* unsigned 64-bitvalue */
存在对应的有符号类型, 但是很少需要;如果你需要它们, 只要在名字里用 s 代替 u.
【为什么内核很少需要有符号类型?】
接口特定的类型
内核中一些通常使用的数据类型有它们自己的typedef 语句, 因此可以顺利移植. 例如,
一个进程标识符 ( pid ) 常常是pid_t 而不是 int. 使用 pid_t 屏蔽了任何在实际数据类型上的不同.
我们使用接口特定的表达式来指一个类型,由一个库定义的, 以便于提供一个接口给一个特定的数据结构.
其他移植性问题
时间间隔
当涉及时间间隔, 不要假定每秒有 1000个嘀哒
无论何时你使用嘀哒来计算时间间隔, 使用 HZ( 每秒的定时器中断数 ) 来标定你的时间
页大小
当使用内存时, 记住一个内存页是PAGE_SIZE 字节, 不是 4KB. 用户态用 getpagesize() 库函数。
如果需要16K内存,不要使用立即数2作为order的值,传给 get_free_pages(). 你需要一个可移植解决方法.
它已经由内核开发者写好并且称为get_order:
#include<asm/page.h>
int order =get_order(16*1024);
buf =get_free_pages(GFP_KERNEL, order);
记住, get_order 的参数必须是 2的幂.
字节序
小心不要假设字节序.
Linux内核定义了一套宏定义来处理之间的转换, 在处理器字节序和你需要以特定字节序存储和加载的数据之间. 例如:
u32 cpu_to_le32 (u32);
u32 le32_to_cpu (u32);
数据对齐
如何读取一个存储于一个不是 4字节倍数的地址的4字节值. i386 用户常常存取不对齐数据项, 但是不是所有的体系允许这个. 很多现代的体系产生一个异常, 每次程序试图不对齐数据传送时;数据传输由异常处理来处理, 带来很大的性能牺牲. 如果你需要存取不对齐的数据, 你应当使用下列宏:
#include<asm/unaligned.h>
get_unaligned(ptr);
put_unaligned(val,ptr);
为了编写可以跨体系移动的数据使用的数据结构,你应当一直强制自然的数据项对齐, 加上对一个特定对齐方式的标准化
编译器可能自己悄悄地插入填充到结构中来保证每个成员是对齐的,
如果你定义一个结构打算来匹配一个设备期望的结构,这个自动的填充可能妨碍你的企图. 解决这个问题的方法是告诉编译器这个结构必须是"紧凑的", 不能增加填充者. 例如, 内核头文件<linux/edd.h> 定义几个与 x86 BIOS 接口的数据结构, 并且它包含下列的定义:
struct
{
u16 id;
u64 lun;
u16 reserved1;
u32 reserved2;
}
__attribute__((packed)) scsi;
如果没有 __attribute__((packed)), lun 成员可能被在前面添加 2 个填充者字节或者 6 个, 如果我们在 64-位平台上编译这个结构.
指针和错误值
很多内部内核函数返回一个指针值给调用者.许多这些函数也可能失败. 大部分情况, 失败由返回一个 NULL 指针值来指示. 这个技术是能用的, 但是它不能通知问题的确切特性.一些接口确实需要返回一个实际的错误码以便于调用者能够基于实际上什么出错来作出正确的判断.
许多内核接口通过在指针值中对错误值编码来返回这个信息.这样的信息必须小心使用, 因为它们的返回值不能简单地与 NULL 比较. 为帮助创建和使用这类接口, 一小部分函数已可用( 在<linux/err.h>).
链表
当使用链表接口时,你应当一直记住列表函数自身不做加锁保护. 如果你的驱动可能试图对同一个列表并发操作, 你有责任实现一个加锁方案. 可选项( 破坏的列表结构, 数据丢失,内核崩溃) 肯定是难以诊断的.
为使用列表机制, 你的驱动必须包含文件<linux/list.h>. 这个文件定义了一个简单的类型 list_head 结构:
struct list_head {struct list_head *next, *prev; };
第12 章 PCI 驱动
## PCI接口
## PCI 寻址
## 启动时间
## 配置寄存器和初始化