现在的位置: 首页 > 综合 > 正文

内存未对齐导致性能降低及其处理

2018年04月16日 ⁄ 综合 ⁄ 共 1950字 ⁄ 字号 评论关闭

  内存未对齐是指cpu要读取N字节数据,但数据的起始地址不能被N所整除,导致效率降低,甚至异常的出现。例如当cpu读取一个int类型的变量,而变量地址是0x10005的时候就产生未对齐访问。

自然对齐:

    N字节的数据类型需要放在起始地址为被N整除的地址这称为自然对齐。并不是所有体系结构的计算机带都要求自然对齐,有的可以指定对齐方式。但是为了达到好的可移植性编写代码的时候最好都用自然对齐方式。

未对齐的影响

    未对齐的内存访问在不同的体系结构中会有不同的效果:有一些计算机可以透明的处理未对齐访问,但效率会降低很多;有一些计算机会产生异常,然后调用一个异常处理程序改正错误;有一些计算机产生异常,但是没有办法校正错误;有一些计算机则没有办法处理未对齐访问,而直接读取不正确的内存地址,造成难以察觉的代码漏洞。

编译器的影响:

    一般情况下编译器可以通过填充(padding)使得数据结构满足对齐要求。例如一下结构:

    Struct foo{

        U16 field1;

        U32 field2;

        U8 field3;

}

假设数据起始地址是0x10000.编译器在field1后填充2个字节使field2保持对齐,最后在field3后填充3个字节使整个数据结构4对齐,全部结构体占用空间为12字节。

导致未对齐访问的原因:

既然编译器能够自动对齐数据结构,导致内存未对齐访问的原因是什么?未对齐访问的

必要条件:指针强制类型转换且小数据转大数据。例如:

unsigned int compare_ether_addr(const u8 *addr1, const u8 *addr2)

{

    const u16 *a = (const u16 *)addr1;

    const u16 *b = (const u16 *)addr2;

    return ((a[0] ^ b[0]) | (a[1] ^b[1]) | (a[2] ^ b[2])) != 0;

}

Addr1指针所在地址可能是奇地址开头,强制类型转化给指针a,导致未对齐的内存访问。Mips架构的cpu不能很好的处理未对齐访问,会导致程序出错退出。X86可以处理未对齐,但是如果未对齐访问太多效率也会下降很多。不管是Mips,还是X86,都希望所操作地址是对齐的,因为这样可以最快速地处理数据。不过X86平台可以很容易很快速地处理不对齐的情况,而Mips一旦遇到地址不对齐的变量就会抛出exception,从而调用一大段后续处理代码,继而消耗大量的时间。

解决方案

应该使用memcpy()函数进行代替指针强制类型转换。

Linux内核对unaligned access的处理方法

可以配置/proc/sys/debug/alignment的参数以配置内核对内存未对齐访问的处理方式,总共有6中可选,"ignored","warn","fixup","fixup+warn","signal","signal+warn",可以配置的值和含义:

0 warn 触发内核打印消息包含程序名称、Pid、PC寄存器值,指令,地址,出错码。

1 fixup 内核尝试修复未对齐访问异常,这会导致性能降低。

2 signal 这个配置会引发SIGBUS错误,程序退出。

请查看内核说明(cat /proc/cpu/alignment)以确定选择那种处理方式所对应的数字。

内存未对齐处理示例

问题描述:

编译webkit源码后在Freeman 9150(mips架构,sh4linux)运行出现unaligned access,SIGBUS错误,程序退出:

     Unaligned userspace access in "QtTestBrowser" pid=1636 pc=0x2a012f6a ins=0x01ce
     Unaligned userspace access in "QtTestBrowser" pid=1636 pc=0x29f4ad7a ins=0x5111
     Sending SIGBUS to "QtTestBrowser" due to unaligned access (PC 29f4ad7a PR 29f4ac6e)
解决办法:

1、  在linux终端输入ulimit –c unlimited开启core dump功能。

2、  修改/proc/sys/kernel/core_uses_pid,使用pid作为core dump文件的扩展名;修改/proc/sys/kernel/core_pattern(例如/corefile/core%p)设置core文件保存位置和文件格式。

3、  使用gdb查看出错信息,定位代码。 gdb ./ QtTestBrowser  core.xxxx, 使用bt命令即可看到程序出错的地方。

4、查看源码,修改错误,注意查看指针转化的情况。用memcpy()代替相关代码。

抱歉!评论已关闭.