【问题标题】:Debugging SIGBUS on x86 Linux在 x86 Linux 上调试 SIGBUS
【发布时间】:2011-01-06 13:06:00
【问题描述】:

什么会导致 Linux 中的通用 x86 用户空间应用程序出现 SIGBUS(总线错误)?我能在网上找到的所有讨论都是关于内存对齐错误的,据我了解,这并不真正适用于 x86。

(我的代码在Geode 上运行,以防那里有任何相关的特定于处理器的怪癖。)

【问题讨论】:

    标签: linux debugging bus-error sigbus


    【解决方案1】:

    x86 Linux 上总线错误的一个常见原因是试图取消引用不是真正的指针或野指针的东西。例如,未能初始化指针,或将任意整数分配给指针然后尝试取消引用它通常会产生分段错误或总线错误。

    对齐确实适用于 x86。即使 x86 上的内存是可字节寻址的(因此您可以有一个指向任何地址的 char 指针),例如,如果您有一个指向 4 字节整数的指针,则该指针必须对齐。

    您应该在 gdb 中运行您的程序并确定哪个指针访问正在生成总线错误以诊断问题。

    【讨论】:

    • 所有 SSE 加载/存储指令都有对齐和未对齐的版本。对于 SSE(128 位)访问,它们实际上在当代 Intel 架构上全速运行,因此仅无条件使用未对齐移动并没有真正的惩罚(除非您正在优化到对齐移动指令的较短长度的水平显着,这不太可能)。
    • 在任何情况下,如果未对齐访问不正确,我会得到 SIGSEGV,而不是 SIGBUS。
    【解决方案2】:

    如果您打开未对齐访问陷阱,您可以从未对齐访问中获取 SIGBUS,但通常在 x86 上是关闭的。如果出现某种错误,您也可以通过访问内存映射设备来获取它。

    最好的办法是使用调试器来识别错误指令(SIGBUS 是同步的),并尝试查看它试图做什么。

    【讨论】:

    • 调试器显示SIGBUS在进入函数后立即发生。也许我有一些内存损坏,或者函数参数之一是坏的?如果错误再次发生,我将不得不检查调试器中的反汇编以获取更多详细信息。
    • @Josh -- 检查实际失败的指令是什么 -- 如果它是 push 或 pop,那么您的堆栈指针已损坏。如果是别的东西,那么指令中的地址就是问题所在。
    【解决方案3】:

    SIGBUS 在 Linux 中发生的原因有很多,除了内存对齐错误 - 例如,如果您尝试访问超出映射文件末尾的 mmap 区域。

    您是否在使用mmap、共享内存区域或类似的东西?

    【讨论】:

    • 是的,我们正在使用共享内存区域。下次出现此错误时,我将调查这种可能性。谢谢。
    • mmap 必须由任何调用 malloc 的程序使用,因为今天 malloc 是 mmap 的转发。
    • @v.oddou:那是匿名mmap,没有“超出映射文件末尾”的概念。
    • @caf 哦哦。好的。
    【解决方案4】:

    哦,是的,还有另一种获取 SIGBUS 的奇怪方法。

    如果内核由于内存压力(必须禁用 OOM 杀手)或 IO 请求失败而无法在代码页中分页,SIGBUS。

    【讨论】:

      【解决方案5】:

      这有点偏离常规,但您可以从未对齐的 SSE2 (m128) 负载中获取 SIGBUS。

      【讨论】:

      • 你可以吗?它通常会导致#GP,它映射到 SIGSEGV。
      【解决方案6】:

      x86(包括 x86_64)Linux 上的 SIGBUS 是一种罕见的野兽。它可能出现在尝试访问超过 mmaped 文件的末尾,或 POSIX 描述的其他一些情况。

      但是由于硬件故障,获得 SIGBUS 并不容易。也就是说,来自任何指令的非对齐访问——无论是否是 SIMD——通常都会导致 SIGSEGV。堆栈溢出导致 SIGSEGV。即使访问非规范形式的地址也会导致 SIGSEGV。所有这一切都是因为#GP 被提出,它几乎总是映射到 SIGSEGV。

      现在,由于 CPU 异常,这里有一些获取 SIGBUS 的方法:

      1. 启用EFLAGS 中的AC 位,然后通过任何内存读取或写入指令进行非对齐访问。详情请见this discussion。

      2. 通过堆栈指针寄存器(rsp 或 rbp)执行规范违规,生成 #SS。这是 GCC 的一个示例(使用 gcc test.c -o test -masm=intel 编译):

      主函数() { __asm__("mov rbp,0x400000000000000\n" "mov rax,[rbp]\n" "ud2\n"); }

      【讨论】:

        【解决方案7】:

        这在上面被简单地称为“失败的 IO 请求”,但我会稍微扩展一下。

        当您使用 ftruncate 懒惰地增长文件、将其映射到内存、开始写入数据然后用完文件系统中的空间时,这是一种常见的情况。映射文件的物理空间在页面错误时分配,如果没有剩余则进程接收 SIGBUS。

        如果您需要您的应用程序正确地从该错误中恢复,那么在 mmap 之前使用 fallocate 显式保留空间是有意义的。在 fallocate 调用后处理 errno 中的 ENOSPC 比处理信号要简单得多,尤其是在多线程应用程序中。

        【讨论】:

          【解决方案8】:

          如果您请求由带有 mmap 和 MAP_HUGETLB 标志的大页面支持的映射,如果内核用完分配的大页面并因此无法处理页面错误,您可以获得 SIGBUS。

          在这种情况下,您需要通过

          提高分配的大页面的数量
          • /sys/kernel/mm/hugepages/hugepages-<size>/nr_hugepages 或
          • /sys/devices/system/node/nodeX/hugepages/hugepages-<size>/nr_hugepages 在 NUMA 系统上。

          【讨论】:

            【解决方案9】:

            当您在 NFS(网络文件系统)上运行二进制文件并且文件已更改时,您可能会看到 SIGBUS。见https://rachelbythebay.com/w/2018/03/15/core/。

            【讨论】:

              猜你喜欢
              • 2010-12-28
              • 2013-04-18
              • 2012-12-30
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-05-11
              • 2010-09-24
              • 1970-01-01
              相关资源
              最近更新 更多