什么可能导致Linux上的通用x86用户态应用程序出现SIGBUS(总线错误)?我在网上找到的所有讨论都是关于内存对齐错误的,根据我的理解,它并不适用于x86。
(我的代码在Geode上运行,以防有任何相关的处理器特定怪癖。)
答案 0 :(得分:19)
SIGBUS
可能在Linux中发生,原因很多 - 例如,如果您尝试访问映射文件末尾之外的mmap
区域。
您使用的是mmap
,共享内存区域或类似内容吗?
答案 1 :(得分:13)
如果打开未对齐的访问陷阱,可以从未对齐的访问中获取SIGBUS,但通常在x86上关闭。如果出现某种错误,您也可以通过访问内存映射设备来获取它。
您最好的选择是使用调试器来识别故障指令(SIGBUS是同步的),并试图查看它试图做的事情。
答案 2 :(得分:8)
x86上的SIGBUS(包括x86_64)Linux是一种罕见的野兽。从尝试访问mmap
ed文件的末尾或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
编译)的示例:
int main() { __asm__("mov rbp,0x400000000000000\n" "mov rax,[rbp]\n" "ud2\n"); }
答案 3 :(得分:4)
哦,是的,还有一种奇怪的方式来获得SIGBUS。
如果内核由于内存压力(必须禁用OOM杀手)或IO请求失败而无法在代码页中进行分页,那么SIGBUS。
答案 4 :(得分:2)
上面简要提到这是一个“失败的IO请求”,但我会稍微扩展一下。
一个常见的情况是,当您使用ftruncate懒惰地生成文件时,将其映射到内存中,开始写入数据,然后在文件系统中耗尽空间。映射文件的物理空间是在页面错误上分配的,如果没有,则进程接收到SIGBUS。
如果您需要应用程序正确地从此错误中恢复,则使用fallocate在mmap之前显式保留空间是有意义的。在fallocate调用之后处理errno中的ENOSPC比处理信号简单得多,尤其是在多线程应用程序中。
答案 5 :(得分:2)
在NFS(网络文件系统)上运行二进制文件且文件已更改时,您可能会看到SIGBUS。参见https://rachelbythebay.com/w/2018/03/15/core/。
答案 6 :(得分:0)
如果您使用mmap
和MAP_HUGETLB
标志请求由大页面支持的映射,则如果内核用完分配的大页面而无法处理页面错误,则可以获取SIGBUS
在这种情况下,您需要通过
增加分配的大页面数/sys/kernel/mm/hugepages/hugepages-<size>/nr_hugepages
或/sys/devices/system/node/nodeX/hugepages/hugepages-<size>/nr_hugepages
在NUMA系统上。答案 7 :(得分:-1)
x86 Linux上出现总线错误的常见原因是尝试取消引用一些实际上不是指针的内容,或者是一个狂野指针。例如,未能初始化指针,或者向指针分配任意整数然后尝试取消引用它通常会产生分段错误或总线错误。
对齐确实适用于x86。即使x86上的内存是字节可寻址的(因此你可以有一个指向任何地址的char指针),如果你有一个指向4字节整数的指针,那么该指针必须是对齐的。
您应该在gdb中运行程序并确定哪个指针访问正在生成总线错误以诊断问题。
答案 8 :(得分:-1)
它有点偏离常规路径,但你可以从未对齐的SSE2(m128)负载获得SIGBUS。