【发布时间】:2011-01-06 13:06:00
【问题描述】:
什么会导致 Linux 中的通用 x86 用户空间应用程序出现 SIGBUS(总线错误)?我能在网上找到的所有讨论都是关于内存对齐错误的,据我了解,这并不真正适用于 x86。
(我的代码在Geode 上运行,以防那里有任何相关的特定于处理器的怪癖。)
【问题讨论】:
标签: linux debugging bus-error sigbus
什么会导致 Linux 中的通用 x86 用户空间应用程序出现 SIGBUS(总线错误)?我能在网上找到的所有讨论都是关于内存对齐错误的,据我了解,这并不真正适用于 x86。
(我的代码在Geode 上运行,以防那里有任何相关的特定于处理器的怪癖。)
【问题讨论】:
标签: linux debugging bus-error sigbus
x86 Linux 上总线错误的一个常见原因是试图取消引用不是真正的指针或野指针的东西。例如,未能初始化指针,或将任意整数分配给指针然后尝试取消引用它通常会产生分段错误或总线错误。
对齐确实适用于 x86。即使 x86 上的内存是可字节寻址的(因此您可以有一个指向任何地址的 char 指针),例如,如果您有一个指向 4 字节整数的指针,则该指针必须对齐。
您应该在 gdb 中运行您的程序并确定哪个指针访问正在生成总线错误以诊断问题。
【讨论】:
如果您打开未对齐访问陷阱,您可以从未对齐访问中获取 SIGBUS,但通常在 x86 上是关闭的。如果出现某种错误,您也可以通过访问内存映射设备来获取它。
最好的办法是使用调试器来识别错误指令(SIGBUS 是同步的),并尝试查看它试图做什么。
【讨论】:
SIGBUS 在 Linux 中发生的原因有很多,除了内存对齐错误 - 例如,如果您尝试访问超出映射文件末尾的 mmap 区域。
您是否在使用mmap、共享内存区域或类似的东西?
【讨论】:
哦,是的,还有另一种获取 SIGBUS 的奇怪方法。
如果内核由于内存压力(必须禁用 OOM 杀手)或 IO 请求失败而无法在代码页中分页,SIGBUS。
【讨论】:
这有点偏离常规,但您可以从未对齐的 SSE2 (m128) 负载中获取 SIGBUS。
【讨论】:
x86(包括 x86_64)Linux 上的 SIGBUS 是一种罕见的野兽。它可能出现在尝试访问超过 mmaped 文件的末尾,或 POSIX 描述的其他一些情况。
但是由于硬件故障,获得 SIGBUS 并不容易。也就是说,来自任何指令的非对齐访问——无论是否是 SIMD——通常都会导致 SIGSEGV。堆栈溢出导致 SIGSEGV。即使访问非规范形式的地址也会导致 SIGSEGV。所有这一切都是因为#GP 被提出,它几乎总是映射到 SIGSEGV。
现在,由于 CPU 异常,这里有一些获取 SIGBUS 的方法:
启用EFLAGS 中的AC 位,然后通过任何内存读取或写入指令进行非对齐访问。详情请见this discussion。
通过堆栈指针寄存器(rsp 或 rbp)执行规范违规,生成 #SS。这是 GCC 的一个示例(使用 gcc test.c -o test -masm=intel 编译):
【讨论】:
这在上面被简单地称为“失败的 IO 请求”,但我会稍微扩展一下。
当您使用 ftruncate 懒惰地增长文件、将其映射到内存、开始写入数据然后用完文件系统中的空间时,这是一种常见的情况。映射文件的物理空间在页面错误时分配,如果没有剩余则进程接收 SIGBUS。
如果您需要您的应用程序正确地从该错误中恢复,那么在 mmap 之前使用 fallocate 显式保留空间是有意义的。在 fallocate 调用后处理 errno 中的 ENOSPC 比处理信号要简单得多,尤其是在多线程应用程序中。
【讨论】:
如果您请求由带有 mmap 和 MAP_HUGETLB 标志的大页面支持的映射,如果内核用完分配的大页面并因此无法处理页面错误,您可以获得 SIGBUS。
在这种情况下,您需要通过
提高分配的大页面的数量/sys/kernel/mm/hugepages/hugepages-<size>/nr_hugepages 或/sys/devices/system/node/nodeX/hugepages/hugepages-<size>/nr_hugepages 在 NUMA 系统上。【讨论】:
当您在 NFS(网络文件系统)上运行二进制文件并且文件已更改时,您可能会看到 SIGBUS。见https://rachelbythebay.com/w/2018/03/15/core/。
【讨论】: