编写可移植代码,需要考虑的一个问题是如何存取不对齐的数据。
例如, 如何读取一个存储于一个不是 4 字节倍数的地址的4字节值. i386 用户常常存取不对齐数据项, 但是不是所有的体系允许这个,很多现代的CPU体系会产生异常。
可以采用以下的函数,进行字节对齐。
#include <asm/unaligned.h>
get_unaligned(ptr);
put_unaligned(val, ptr);
这些宏是无类型的, 并且用在每个数据项, 不管它是 1 个, 2 个, 4 个, 或者 8 个字节长.
关于对齐的另一个问题是跨平台的数据结构移植性. 同样的数据结构可能在不同的平台上编译方式不同,编译器根据各个平台不同的惯例来安排结构成员对齐。
为了编写可以跨不同CPU体系使用的数据结构,你应当一直强制自然的数据项对齐, 加上对一个特定对齐方式的标准化. 自然对齐意味着存储数据项在是数据项大小的整数倍的地址上(例如, 8-byte 项在 8 的整数倍的地址上)。
为了阻止编译器以不希望的方式调整成员变量存储位置,你应当使用填充成员来避免在数据结构中留下空洞。
为展示编译器如何强制对齐, dataalign 程序在源码的 misc-progs 目录中发布, 并且一个对等的 kdataalign 模块是 misc-modules 的一部分. 这是程序在几个平台上的输出以及模块在 SPARC64 的输出:
arch Align: char short int long ptr long-long u8 u16 u32 u64 i386 1 2 4 4 4 4 1 2 4 4 i686 1 2 4 4 4 4 1 2 4 4 alpha 1 2 4 8 8 8 1 2 4 8 armv4l 1 2 4 4 4 4 1 2 4 4 ia64 1 2 4 8 8 8 1 2 4 8 mips 1 2 4 4 4 8 1 2 4 8 ppc 1 2 4 4 4 8 1 2 4 8 sparc 1 2 4 4 4 8 1 2 4 8 sparc64 1 2 4 4 4 8 1 2 4 8 x86_64 1 2 4 8 8 8 1 2 4 8 kernel: arch Align: char short int long ptr long-long u8 u16 u32 u64 kernel: sparc64 1 2 4 8 8 8 1 2 4 8
有趣的是注意不是所有的平台对齐 64-位值在 64-位边界上, 因此你需要填充者成员来强制对齐和保证可移植性。
最后, 需要知道编译器可能自己悄悄地插入填充成员到数据结构中,用来保证每个成员是对齐的。
为了目标处理器的良好性能. 自动填充可能妨碍你的企图. 解决这个问题的方法是告诉编译器这个结构必须是"紧凑的", 不能增加填充者。
例如, 内核头文件 <linux/edd.h> 定义几个与 x86 BIOS 接口的数据结构,定义如下:
struct { u16 id; u64 lun; u16 reserved1; u32 reserved2; } __attribute__ ((packed)) scsi;
在64-位CPU平台上编译这个结构时,如果没有使用__attribute__ ((packed)),在lun 成员前面,可能添加 2 个或者6 个字节进行填充。