分类: 嵌入式
2016-06-16 13:06:56
1 内存对齐的概念
现代计算机内存空间都是按照byte划分的,理论上对任何类型变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量时,经常在特定的内存地址访问(即对齐),需要对齐的原因
1)移植原因:不是所有硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则就会出现硬件异常。
2)性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐,原因是,未对齐的内存,处理器需要访问两次内存;对齐的内存,仅需访问一次内存。
2 对齐规则
硬件平台编译器都有默认的“对齐系数”,程序员可以通过预编译命令#pragma
pack(n),n=1,2,4,8,16来设定这一系数,对齐规则:
1)数据成员对齐规则:结构(struct)和联合(union)的数据成员,第一个数据成员放在偏移量为0的地方,后续每个数据成员的对齐按照#pragma
pack指定的数值和这个数据成员自身长度中,比较小的那个对齐,即各成员变量存放的起始地址相对于结构起始地址的偏移量必须为该变量的数据类型所占用字节数的整数倍。
2)
结构或联合的整体对齐规则:在数据成员完成各自对齐之后,结构或联合本身也要进行对齐,对齐将按照#pragma
pack指定的数值和结构或联合最大数据成员长度中,比较小的那个对齐。
结合1)和2)可得出如下结论:
a)如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,
b)如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。
3 ARM对齐问题
ARM指令每执行一条指令,PC的值加4个字节(32bits),一次访问4字节内容,该字节的起始地址必须是4字节对齐的位置上,即地址的低两位为bits[0b00],也就是说地址必须是4的倍数。
注:按字对齐,地址的最低二位必须为0,即bits[0b00];
按半字对齐,地址的最低一位必须为0,即bits[0b0];
按字节对齐,地址值任意
4 ARM字节对齐关键字
1)
__align(num)用于修改最高级别对象的字节边界。
在汇编中使用LDRD或者STRD时,就用命令__align(8)进行修饰限制,保证数据对象是相应对齐,修饰对象的命令最大是8个字节限制,可让2字节的对象按4字节对齐,但不能让4字节的对象2字节对齐。__align是存储类修改,他只修饰最高级类型对象不能用于结构或者函数对象。
2)
__packed 进行一字节对齐。
不能对packed的对象进行对齐;
所有对象的读写访问都进行非对齐访问;
float及包含float的结构联合及未用__packed的对象将不能字节对齐;
__packed对局部整形变量无影响;
强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定义为: packed __packed int* p; //__packed int 则没有意义。
3) __unaligned 用于修饰该变量可按照非对齐访问。
5 字节对齐问题的排除
如果出现对齐或者赋值问题,首先查看编译器的big little端设置;然后查看这种体系本身是否支持非对齐访问,如果支持,看设置了对齐与否,如果没有,则看访问时需要加某些特殊的修饰来标志其特殊访问操作。
6 结论
针对于32位处理器对于本地使用的数据结构,为提高内存访问效率,采用四字节对齐方式;同时为了减少内存的开销,合理安排结构成员的位置,减少四字节对齐导致的成员之间的空隙,降低内存开销。
对于处理器之间的数据结构,需要保证消息的长度不因为在不同编译平台和不同处理器导致消息结构的长度发生变化,使用一字节对齐方式对消息结构进行紧缩;为保证处理器之间的消息的数据结构的内存访问效率,采用字节填充的方式自己对消息中成员进行四字节对齐。
数据结构的成员位置要兼顾成员之间的关系、数据访问效率和空间利用率。顺序安排的原则是:四字节的放在最前面,两字节的紧接最后一个四字节成员,一字节紧接最后一个两字节成员,填充字节放在最后。举例如下:
typedef
struct tag_T_MSG{
long ParaA;
long ParaB;
short ParaC;
char
ParaD;
char Pad;
} T_MSG;