1.内存对齐
1.1 内存对齐概要
现代计算机中内存空间都是按照byte划分的,从理论上讲对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

1.2 内存对齐作用和原因
各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台的要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为 32位)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。

2.ARM平台下内存对齐
在ARM中,有ARM和Thumb两种指令。ARM指令:每执行一条指令,PC的值加4个字节(32bits),一次访问4字节内容,该字节的起始地址必须是4字节对齐的位置上,即地址的低两位为bits[0b00],也就是说地址必须是4的倍数。Thumb指令:每执行一条指令,PC的值加2个字节(16bits),一次访问2字节内容,该字节的起始地址必须是2字节对齐的位置上,即地址的低两位为bits[0b0],也就是说地址必须是2的倍数。

遵循以上方式叫对齐(aligned)存储访问操作,不遵守这样方式称为非对齐(unaligned)存储访问操作。SylixOS下的ARM平台遵守对齐方式。

ARM平台下由于内存对齐产生的问题,如程序清单 2.1,是一段由于ARM平台下遵守内存对齐访问产生问题的代码,代码是一个简单的宏定义将VAL值赋值到DATA地址上。在程序中我们无法保证传进的参数DATA是4的整数倍,所以导致了会出现内存访问异常的现象。程序在运行中出现地址访问错误后退出。

程序清单2.1 平台下问题代码

#define EC_WRITE_U32(DATA, VAL) \

    do { \

        *((uint32_t *) (DATA)) = cpu_to_le32((uint32_t) (VAL)); \

} while (0)      

3.ARM平台下解决方案
上述问题可以修改应用层代码去避免此类问题。如程序清单3.1,我们定义宏如果不是X86平台,直接将uint32_t内存地址强制转换成uint8_t地址,再将数据VAL强制拆分成4个uint8_t型数据分别赋值到对应的uint8_t内存地址上。
程序清单3.1 ARM平台下避免字节对齐访问

#ifdef X86_PLATFORM

#define EC_WRITE_U32(DATA, VAL) \

do { \

       *((uint32_t *) (DATA)) = cpu_to_le32((uint32_t) (VAL)); \

    } while (0)

#else

#define EC_WRITE_U32(DATA, VAL) \

do { \

      *((uint8_t *) (DATA)) = (cpu_to_le32((uint32_t) (VAL))) & 0xff

       *(((uint8_t *) (DATA)) + 1) =(cpu_to_le32((uint32_t)(VAL)) >> 8) & 0xff; \

     *(((uint8_t *) (DATA)) + 2) = (cpu_to_le32((uint32_t) (VAL)) >> 16) & 0xff; \

     *(((uint8_t *) (DATA)) + 3) = (cpu_to_le32((uint32_t) (VAL)) >> 24) & 0xff; \

   } while (0)

   …