分类:
2011-12-15 23:07:23
原文地址:ldd3学习之十:内核数据类型 作者:leon_yu
尽管概念上地址是指针,但使用一个无符号整型可以更好地实现内存管理; 内核把物理内存看成一个巨型数组, 内存地址就是该数组的索引。 我们可以方便地对指针取值,但直接处理内存地址时,我们几乎从不会以这种方式对他取值。使用一个整数类型避免了这种取值,因此避免了 bug。所以,利用至少在 Linux 目前支持的所有平台上,指针和长整型始终是相同大小的这一事实,内核中内存地址常常是 unsigned long。
C99 标准定义了 intptr_t 和 uintptr_t 类型,它们是能够保存指针值的整型变量。但没在 2.6 内核中几乎没使用。
2.确定大小的类型
当需要知道你定义的数据的大小时,可以使用内核提供的下列数据类型(所有的数据声明在
若一个用户空间程序需要使用这些类型,可在符号前加一个双下划线: __u8和其它类型是独立于 __KERNEL__ 定义的。
这些类型是 Linux 特定的,它们妨碍了移植软件到其他的 Unix 机器。新的编译器系统支持 C99-标准 类型,如 uint8_t 和 uint32_t。若考虑移植性,使用这些类型比 Linux特定的变体要好。
3.接口特定的类型(_t 类型)
内核中最常用的数据类型由它们自己的 typedef 声明,阻止了任何移植性问题。“接口特定(interface-specific)”由某个库定义的一种数据类型, 以便为了某个特定的数据结构提供接口。很多 _t 类型在
注意:近来已经很少定义新的接口特定的类型。有许多内核开发者已经不再喜欢使用 typedef 语句,他们宁愿看到代码中直接使用的真实类型信息。很多老的接口特定类型在内核中保留,他们不会很快消失。
即使没有定义接口特定类型,也应该始终是用和内核其他部分保持一致、适当的数据类型。只要驱动使用了这种“定制”类型的函数,但又不遵照约定,编译器会发出警告,这时使用 -Wall 编译器选项并小心去除所有的警告,就可以确信代码的可移植性了。
_t 类型的主要问题是:打印它们时,常常不容易选择正确的 printk 或 printf 格式。打印接口特定的数据的最好方法是:将其强制转换为可能的最大类型(常常是 long 或 unsigned long ) 并用相应的格式打印。
4.其他移植性问题
移植的一个通常规则是:避免使用显式的常量值,要使用预处理宏使常量值参数化。
①时间间隔
当处理时间间隔时,不要假定每秒的jiffies个数,不是每个 Linux 平台都以固定的速度运行.当计算时间间隔时,要使用 HZ ( 每秒的定时器中断数 ) 来标定你的时间.比如0.5S--HZ/2.
②页大小
当使用内存时,记住一个内存页是 PAGE_SIZE 字节, 不是 4KB。相关的宏定义是 PAGE_SIZE 和 PAGE_SHIT(包含将一个地址移位来获得它的页号的位数),在
若一个驱动需要 16 KB 来暂存数据,一个可移植得解决方法是 get_order:
③字节序
不要假设字节序:代码应该编写成不依赖所操作数据的字节序的方式。
头文件
在<linux/byteorder/big_endian.h>中定义了__BIG_ENDIAN ,而在
但是还有一个更好的方法:Linux 内核有一套宏定义来处理处理器字节序和特定字节序之间的转换。例如:
④数据对齐
编写可移植代码而值得考虑的最后一个问题是如何访问未对齐的数据。存取不对齐的数据应当使用下列宏:
关于对齐的另一个问题是数据结构的跨平台移植性。同样的数据结构在不同的平台上可能被不同地编译。为了编写可以跨体系移植的数据结构,应当始终强制数据项的自然对齐。自然对齐(natural alignment)指的是:数据项大小的整数倍的地址上存储数据项。 应当使用填充符避免强制自然对齐时编译器移动数据结构的字段,在数据结构中留下空洞。
为了目标处理器的良好性能,编译器可能悄悄地插入填充符到结构中,来保证每个成员是对齐的。若定义一个和设备要求的结构体相匹配结构,自动填充符会破坏这个意图。解决这个问题的方法是告诉编译器这个结构必须是"紧凑的", 不能增加填充符。例如下列的定义:
⑤指针和错误值
许多内核接口通过将错误值编码到指针值中来返回错误信息。这样的信息必须小心使用,因为它们的返回值不能简单地与 NULL 比较。为帮助创建和使用这类接口,
5.链表
链表详见:http://blog.chinaunix.net/space.php?uid=24708340&do=blog&id=3235017