字节对齐 __attribute__((packed))

来源:互联网 发布:腾讯充值软件 编辑:程序博客网 时间:2024/05/16 14:57


 

struct {         u16 id;         u64 lun;         u16 reserved1;         u32 reserved2; } __attribute__ ((packed)) scsi; //如果没有packed默认是字节对齐的如果没有 __attribute__ ((packed)), lun 成员可能被在前面添加 2 个填充者字节或者 6 个, 如果我们在 64-位平台上编译这个结构.

需要数据对齐的根本原因在于CPU访问数据的效率问题。假设上面整型变量的地址不是自然对齐,比如为0x00000002,则CPU如果取它的值的话需要访问两次内存,第一次取从0x00000002-0x00000003的一个short,第二次取从0x00000004-0x00000005的一个short然后组合得到所要的数据。

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

编写可移植代码而值得考虑的最后一个问题是如何存取不对齐的数据 -- 例如, 如何读取
一个存储于一个不是 4 字节倍数的地址的 4 字节值. i386 用户常常存取不对齐数据项,
但是不是所有的体系允许这个. 很多现代的体系产生一个异常, 每次程序试图不对齐数据
传送时; 数据传输由异常处理来处理, 带来很大的性能牺牲. 如果你需要存取不对齐的数
据, 你应当使用下列宏:
#include <asm/unaligned.h>
get_unaligned(ptr);
put_unaligned(val, ptr);

#define get_unaligned __get_unaligned_le#define put_unaligned __put_unaligned_le#define __get_unaligned_le(ptr) ((__force typeof(*(ptr)))({__builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr),__builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)),__builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)),__builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)),__bad_unaligned_access_size()))));}))#define __put_unaligned_le(val, ptr) ({\void *__gu_p = (ptr);\switch (sizeof(*(ptr))) {\case 1:\*(u8 *)__gu_p = (__force u8)(val);\break;\case 2:\put_unaligned_le16((__force u16)(val), __gu_p);\break;\case 4:\put_unaligned_le32((__force u32)(val), __gu_p);\break;\case 8:\put_unaligned_le64((__force u64)(val), __gu_p);\break;\default:\__bad_unaligned_access_size();\break;\}\(void)0; })


这些宏是无类型的, 并且用在每个数据项, 不管它是 1 个, 2 个, 4 个, 或者 8 个字节
长. 它们在任何内核版本中定义.
关于对齐的另一个问题是跨平台的数据结构移植性. 同样的数据结构( 在 C-语言 源文件
中定义 )可能在不同的平台上不同地编译. 编译器根据各个平台不同的惯例来安排结构成
员对齐.
为了编写可以跨体系移动的数据使用的数据结构, 你应当一直强制自然的数据项对齐, 加
上对一个特定对齐方式的标准化. 自然对齐意味着存储数据项在是它的大小的整数倍的地
址上(例如, 8-byte 项在 8 的整数倍的地址上). 为强制自然对齐在阻止编译器以不希望
的方式安排成员量的时候, 你应当使用填充者成员来避免在数据结构中留下空洞.

#pragma pack(1) //让编译器对这个结构作1字节对齐struct test{char x1;short x2;float x3;char x4;};#pragma pack() //取消1字节对齐,恢复为默认4字节对齐这时候sizeof(struct test)的值为8。例3#define GNUC_PACKED __attribute__((packed))struct PACKED test{char x1;short x2;float x3;char x4;}GNUC_PACKED;这时候sizeof(struct test)的值仍为8。
先让我们看几个例子吧(32bit,x86环境,gcc编译器):设结构体如下定义:struct A{        int a;        char b;        short c;};struct B{        char b;        int a;        short c;};现在已知32位机器上各种数据类型的长度如下:char:1(有符号无符号同)   short:2(有符号无符号同)   int:4(有符号无符号同)   long:4(有符号无符号同)   float:4        double:8那么上面两个结构大小如何呢?结果是:sizeof(strcut A)值为8sizeof(struct B)的值却是12结构体A中包含了4字节长度的int一个,1字节长度的char一个和2字节长度的short型数据一个,B也一样;按理说A,B大小应该都是7字节。之所以出现上面的结果是因为编译器要对数据成员在空间上进行对齐。上面是按照编译器的默认设置进行对齐的结果,那么我们是不是可以改变编译器的这种默认对齐设置呢,当然可以.例如:#pragma pack (2) /*指定按2字节对齐*/struct C{        char b;        int a;        short c;};#pragma pack () /*取消指定对齐,恢复缺省对齐*/sizeof(struct C)值是8。修改对齐值为1:#pragma pack (1) /*指定按1字节对齐*/struct D{        char b;        int a;        short c;};#pragma pack () /*取消指定对齐,恢复缺省对齐*/sizeof(struct D)值为7。后面我们再讲解#pragma pack()的作用.





0 0
原创粉丝点击