当前位置:  开发笔记 > 开发工具 > 正文

x86上的错误对齐指针

如何解决《x86上的错误对齐指针》经验,为你挑选了2个好方法。

有人提供一个示例是否会因为错位而将指针从一种类型转换为另一种类型失败?

在对这个答案的评论中,两者都表示做了类似的事情

char * foo = ...;
int bar = *(int *)foo;

如果启用了对齐检查,即使在x86上也可能导致错误.

set $ps |= (1<<18)在GDB中设置对齐检查标志后尝试生成错误条件,但没有任何反应.

工作(即非工作;))示例是什么样的?


答案中没有任何代码片段在我的系统上失败 - 我将尝试使用不同的编译器版本,稍后在不同的PC上.

顺便说一句,我自己的测试代码看起来像这样(现在也使用asm来设置AC标志和未对齐的读写):

#include 

int main(void)
{
    #ifndef NOASM
    __asm__(
        "pushf\n"
        "orl $(1<<18),(%esp)\n"
        "popf\n"
    );
    #endif

    volatile unsigned char foo[] = { 1, 2, 3, 4, 5, 6 };
    volatile unsigned int bar = 0;

    bar = *(int *)(foo + 1);
    assert(bar == 0x05040302);

    bar = *(int *)(foo + 2);
    assert(bar == 0x06050403);

    *(int *)(foo + 1) = 0xf1f2f3f4;
    assert(foo[1] == 0xf4 && foo[2] == 0xf3 && foo[3] == 0xf2 &&
        foo[4] == 0xf1);

    return 0;
}

断言传递没有问题,即使生成的代码肯定包含未对齐的访问mov -0x17(%ebp), %edxmovl $0xf1f2f3f4,-0x17(%ebp).


那么设置是否会AC触发SIGBUS?我无法在Windows XP下运行我的英特尔双核笔记本电脑而没有我测试的GCC版本(MinGW-3.4.5,MinGW-4.3.0,Cygwin-3.4.4),而codelogic和Jonathan Leffler在x86上提到了失败......



1> Michael Burr..:

在未对齐访问将导致x86出现问题(除了使内存访问需要更长时间)之外,这种情况并不常见.以下是我听过的一些内容:

    您可能不会将此视为x86问题,但SSE操作会从对齐中受益.对齐数据可用作存储器源操作数以保存指令.在Nehalem之前,未对齐加载指令movupsmovaps在微体系结构上慢,但在Nehalem和更高版本(以及AMD Bulldozer系列)上,未对齐的16字节加载/存储与未对齐的8字节加载/存储一样有效; 如果数据恰好在运行时对齐或者没有跨越缓存行边界,则单个uop并且没有任何惩罚,否则高效的硬件支持缓存行分割.4k分裂非常昂贵(约100个周期)直到Skylake(低至~10个周期,如高速缓存线分割).有关详细信息,请参阅x86标记wiki中的https://agner.org/optimize/和性能链接.

    如果它们没有充分对齐,则互锁操作(例如lock add [mem], eax)非常慢,特别是如果它们跨越缓存行边界,因此它们不能仅在CPU内核中使用缓存锁定.在较旧的(有缺陷的)SMP系统上,它们实际上可能无法成为原子(参见https://blogs.msdn.com/oldnewthing/archive/2004/08/30/222631.aspx).

    Raymond Chen讨论的另一种可能性是处理具有硬件存储内存的设备(当然是一种奇怪的情况) - https://blogs.msdn.com/oldnewthing/archive/2004/08/27/221486.aspx

    我记得(但没有参考 - 所以我不确定这个)类似的问题与未对齐的访问跨越页面边界,也涉及页面错误.我会看看我是否可以为此挖掘参考.

在研究这个问题时我学到了一些新东西(我想知道$ps |= (1<<18)几个地方提到的"GDB命令").我没有意识到x86 CPU(从486开始)能够在执行错位访问时导致异常.

来自Jeffery Richter的"Windows编程应用程序,第4版":

让我们仔细看看x86 CPU如何处理数据对齐.x86 CPU在其EFLAGS寄存器中包含一个称为AC(对齐检查)标志的特殊位标志.默认情况下,当CPU首次接通电源时,此标志设置为零.当此标志为零时,CPU会自动执行任何操作,以便成功访问未对齐的数据值.但是,如果此标志设置为1,则只要尝试访问未对齐的数据,CPU就会发出INT 17H中断.x86版本的Windows 2000和Windows 98从不改变此CPU标志位.因此,当应用程序在x86处理器上运行时,您永远不会看到应用程序中发生数据错位异常.

这对我来说是新闻.

当然,访问错位的一个大问题是,当你最终编译非x86/x64处理器的代码时,你最终必须追踪并修复一大堆东西,因为几乎所有其他32位或更大的处理器对对齐问题很敏感.


对"互锁操作的微小修正必须对对齐的数据进行操作,以确保它们在多处理器系统上是原子的".互锁操作将对X86上的未对齐数据起作用,它们碰巧有更慢的边缘情况,但代码不应该崩溃.FWIW,您不必在某些PowerPC上使用完全对齐进行互锁操作(例如,Microsoft制造的某个游戏系统将处理只有32位对齐的64位互锁).
我只是把它弄好了 - Penryn未对齐的SSE操作仍然具有对齐读取的延迟的大约三倍,尽管它们的吞吐量比以前的核心更好.这与您描述的行为(以及VMX处理它的方式,例如加载负载shuffle)一致.

2> 小智..:

如果您阅读了Core I7架构(特别是他们的优化文献),那么英特尔实际上已经在其中放置了一个TON硬件,以使未对齐的内存访问几乎免费.据我所知,只有跨越高速缓存行边界的错位才有任何额外成本 - 即便如此,它也是最小的.就我记忆而言,AMD在访问错位(循环方式)方面也没有什么问题(虽然已经有一段时间了).

为了它的价值,我确实在eflags(AC位 - 对齐检查)中设置了那个标志,当我被带走时优化我正在进行的项目.事实证明,窗口是完全未对齐的访问 - 很多,以至于我无法在我们的代码中找到任何未对齐的内存访问,我被库和Windows代码中的这么多未对齐的访问轰炸,我没有时间继续.

也许我们可以了解到,当CPU使产品免费或成本非常低时,程序员会变得自满并做一些额外开销的事情.也许英特尔的工程师做了一些调查,发现典型的x86桌面软件每秒会进行数百万次错位访问,因此他们在CoreI7中放置了极其快速错位的访问硬件.

HTH

推荐阅读
wangtao
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有