📄 (ldd) ch10-合理使用数据类型(转载).txt
字号:
其它与移植有关的问题
除了数据类型定义问题之外,如果想让你编写的驱动程序能在不同的Linux平台
间移植的话,还必须注意到其它一些软件上的问题:
时间间隔
在处理时间间隔时,不能假定每秒一定有100个jiffy。虽然对当前的Linux-x86而言这是
对的,但并不是所有Linux平台都是以100HZ运行。如果你改变了HZ的数值,那么即使对x
86,这种假设也是错误的,何况没人知道未来的内核会发生些什么变化。使用jiffy计算
时间间隔的时候,应该把时间转换成以HZ为单位。例如,为了检测半秒钟的超时,可以
把消逝的时间和HZ/2作比较。更常见的,与msec毫秒对应的jiffy的数目总是msec*HZ/10
00。许多的网络驱动程序在移植到Alpha上时都必须修正该细节;有些开始是为PC设计的
驱动程序给超时明确定义了一个jiffy值,但是Alpha却有着不同的HZ数值。
页大小
使用内存时,要记住内存页的大小为PAGE_SIZE字节,而不是4KB。假设页大小就是4KB并
硬编码该数值是PC程序员常犯的错误-Alpha页大小是这的两倍。相关的宏有PAGE_SIZE
和PAGE_SHIFT。后者包含要得到一个地址所在页的页号时需要对该地址右移的位数。对
和PAGE_SHIFT。后者包含要得到一个地址所在页的页号时需要对该地址右移的位数。对
当前的4KB和8KB的页,这个数值通常是12或者13。这些宏在头文件<asm/page.h>中定义
。
让我们来看一种简单的情况。如果驱动程序需要16KB空间来存放临时数据,它不应当指
定get_free_pages函数的参数order(“2”的幂)。需要一种可移植的解决办法。此时,
可以使用条件编译#ifdef __alpha__,但这只适用于已知的平台,而如果要支持别的平
台,它就不能奏效了。我建议使用下面的代码:
buf = get_free_pages(GFP_KERNEL, 14 - PAGE_SHIFT, 0 /*dma*/)
;
或者,更好一些的代码:
int order = (14 - PAGE_SHIFT >0) ? 14 - PAGE_SHIFT : 0;
int order = (14 - PAGE_SHIFT >0) ? 14 - PAGE_SHIFT : 0;
buf = get_free_pages(GFP_KERNEL, order, 0 /*dma*/);
两种解决办法都利用了16KB等于1<<14这一常识。两个数的商就是它们对数的差(的幂),
而14和PAGE_SHIFT都是幂。第二种解决办法就更好,因为它可以防止把一个负的order值
传递给get_free_pages函数;order值时在编译时就计算好的,没有运行时的额外开销,
而且,上面给出的实现方法是不依赖于PAGE_SIZE来分配任何2的幂次大小的内存空间的
安全方法。
字节序
要小心的是不要主观假设字节序。虽然PC是按低字节优先的方式存储多个字节(“小印地
安,little endian”),但是大多数更高级的平台是以另一种方式工作的(“大印地安,
big endian”)。虽然好的程序不会依赖于字节序,但有时驱动程序需要创建占一个字节
以上的整数,或者相反(一个字节以下)。此时,代码中就应该将头文件<asm/byteorder.
h>包含进来,并且检测头文件中是否定义了__BIG_ENDIAN或__LITTLE_ENDIAN。起始的下
划线在Linux-1.2之后版本的头文件中却去掉了,在头文件<asm/byteorder.h>后再包含s
cull示例程序中的头文件sysdep.h就可以修正这个不兼容。
cull示例程序中的头文件sysdep.h就可以修正这个不兼容。
当字节序相关问题与网络传输有联系的时候,就应当使用下面各种函数来进行16位和32
位数值的转换,这些函数也都是在头文件<asm/byteorder.h>中定义的:
unsigned long ntohl(unsigned long);
unsigned short ntohs(unsigned short);
unsigned long htonl(unsigned long);
unsigned short htons(unsigned short);
在网络程序员当中,这些函数是众所周知的。它们得名于“Network TO Host Long”(从
网络到主机的long类型)或类似的短语。
2.1.10版的内核增加了cpu-to-little-endian和cpu-to-big-endian两种转换,2.1.43版
的内核在这方面又加以扩充。新增的一些实用函数将在第17章“近期发展”中的“转换
函数”一节中描述。
数据对齐
在编写可移植代码时最后一个值得考虑的问题是如何访问未对齐数据-例如,当一个4字
节的数值被储存在不是4字节的整数倍的地址中时,如何将它读出来。PC的用户常常访问
未对齐的数据项,但并不是所有体系结构都允许这样做。举个例子,在Alpha上,每当程
序试图传送未对齐数据时,都会产生一个异常。如果你需要访问未对齐数据,可以使用
下面这些宏:
#include <asm/unaligned.h>
get_unaligned(ptr);
put_unaligned(val,ptr);
put_unaligned(val,ptr);
这些宏是与类型无关的。对各种数据项,不管它是1字节,2字节,4字节还是8字节,这
些宏都有效。在2.0版以前的内核并不提供这些宏,但在1.2版的内核在头文件sysdep.h
中对它们作了定义。
一个通用准则就对显式的常数值持怀疑态度。通常,使用预编译的宏来使代码参数化使
代码更通用。虽然我不能在此列出所有参数化的值,但你可以在头文件中找到正确的提
示。
不幸的是,有些地方问题还没有得到解决,例如对磁盘扇区数据的处理。出于历史的原
因,Linux只能处理.5KB的磁盘扇区。所幸的是,现存所有设备都满足这个限制。目前正
在逐渐地改进代码以支持不同的扇区大小。但要找到代码中所有在.5KB的假设下进行硬
编码的地方却非常困难。扇区大小的问题将在第12章“加载块设备驱动程序”中进一步
描述。
快速参考
快速参考
在本章引入了如下一些符号:
#include <linux/types.h>
typedef u8;
typedef u16;
typedef u32;
typedef u64;
这些类型保证是8-、16-、32-和64-位的无符号整数值。对应的有符号类型同样
存在。 在用户空间,你可以通过__u8,__u16等来引用这些类型。
#include <asm/page.h>
PAGE_SIZE
PAGE_SIZE
PAGE_SHIFT
这些符号定义了当前体系结构下每页包含的字节数和页偏移量所占位数(12对应4KB的页
而13对应8KB的页)。
#include <asm/byteorder.h>
__LITTLE_ENDIAN
__BIG_ENDIAN
两个符号中只能定义其一,这依赖于体系结构。版本1.3.18和更老的版本中也声明了这
些符号,但是没有打头的下划线(因而和一些网络部分的头文件会发生冲突)。
#include <asm/byteorder.h>
unsigned long ntohl(unsigned long);
unsigned long ntohl(unsigned long);
unsigned short ntohs(unsigned short);
unsigned long htonl(unsigned long);
unsigned short htons(unsigned short);
这些函数在网络字节序和主机字节序间转换long类型和short类型的数据。
#include <asm/unaligned.h>
get_unaligned(ptr);
put_unaligned(val,ptr);
一些体系结构需要使用这些宏来保护对未对齐数据的访问。在这些体系结构上,这些宏
扩展为通常的对指针取地址的操作,以允许你访问未对齐的数据。
一些体系结构需要使用这些宏来保护对未对齐数据的访问。在这些体系结构上,这些宏
扩展为通常的对指针取地址的操作,以允许你访问未对齐的数据。
-----------------------------------------------------------------------------
* 读分区表时,执行二进制文件时或者解码一个网络包时,就会发生这种情况。
+ 实际上,即使两种类型仅是同一对象的不同名字,例如PC上的unsigned long和u32类
型,编译器也会发出类型不匹配的信号。
--
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -