总线错误与分段故障

时间:2009-05-08 06:56:42

标签: c segmentation-fault sigsegv bus-error sigbus

总线错误和分段错误之间的区别? 是否会发生程序发出seg故障并第一次停止,第二次发生总线错误并退出?

7 个答案:

答案 0 :(得分:44)

在我使用过的大多数架构中,区别在于:

  • 当您访问不想要的内存时(例如,在您的地址空间之外),会导致SEGV。
  • 由于与CPU的对齐问题导致SIGBUS(例如,尝试从不是4的倍数的地址读取长整数)。

答案 1 :(得分:20)

如果您mmap()一个文件并尝试访问超出文件末尾的映射缓冲区的一部分,以及错误条件(如空间不足),也会引发

SIGBUS 。如果使用sigaction()注册信号处理程序并设置SA_SIGINFO,则可以让程序检查故障内存地址并仅处理内存映射文件错误。

答案 2 :(得分:6)

例如,当您的程序尝试执行硬件总线不支持的操作时,可能会导致总线错误。例如,在SPARCs上,尝试从奇数地址读取多字节值(例如int,32位)会产生总线错误。

例如,当您执行违反分段规则的访问时,即尝试读取或写入您不拥有的内存时,就会发生分段错误。

答案 3 :(得分:4)

我假设你在谈论Posix定义的SIGSEGVSIGBUS信号。

程序引用无效地址时发生

SIGSEGVSIGBUS是实现定义的硬件故障。这两个信号的默认操作是终止程序。

该程序可以捕获这些信号,甚至可以忽略它们。

答案 4 :(得分:2)

将您的问题(可能不正确)解释为“我间歇性地获得SIGSEGV或SIGBUS,为什么不一致?”,值得注意的是,C或C ++标准无法保证用指针做狡猾的事情导致段错;它只是“未定义的行为”,作为我曾经说过的教授,这意味着它可能会导致鳄鱼从地板上冒出来吃掉你。

所以你的情况可能是你有两个错误,第一个发生有时导致SIGSEGV,第二个(如果没有发生segfault并且程序仍在运行)会导致SIGBUS。

我建议您逐步使用调试器,并留意鳄鱼。

答案 5 :(得分:1)

如果不是

,这将是What is a bus error?的重复
  

程序是否会发生seg故障并且第一次停止并且第二次出现总线错误并退出会发生吗?

问题的一部分。您应该能够使用此处的信息自行回答这个问题。


  疯狂:一遍又一遍地做同样的事情并期待不同的结果    - 阿尔伯特爱因斯坦


当然,从字面上理解这个问题......

#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
int main() {
    srand(time(NULL));
    if (rand() % 2)
        kill(getpid(), SIGBUS);
    else
        kill(getpid(), SIGSEGV);
    return 0;
}

Tada,一个程序可以在一次运行时退出分段故障,并在另一次运行时退出并发生总线错误。

答案 6 :(得分:1)

  

程序是否会发生seg故障并且第一次停止并且第二次出现总线错误并退出会发生吗?

是的,即使对于同一个错误:这是一个严肃但简单的例子来自macOS,它可以通过数组边界之外的索引产生分段错误(SIGSEGV)和总线错误(SIGBUS),确定性的方式。上面提到的未对齐访问不是macOS的问题。 (这个例子不会导致任何SIGBUS,如果它在调试器中运行,在我的情况下是lldb!)

bus_segv.c:

#include <stdlib.h>

char array[10];

int main(int argc, char *argv[]) {
    return array[atol(argv[1])];
}

该示例从命令行获取一个整数,该整数用作数组的索引。一些索引值(甚至在数组外)不会产生任何信号。 (给出的所有值都取决于标准的段/段大小。我使用clang-902.0.39.1在High Sierra macOS 10.13.5,i5-4288U CPU @ 2.60GHz上生成二进制文件。)

高于77791且低于-4128的指数将导致分段错误(SIGSEGV)。 24544将导致总线错误(SIGBUS)。这里是完整的地图:

$ ./bus_segv -4129
Segmentation fault: 11
$ ./bus_segv -4128
...
$ ./bus_segv 24543
$ ./bus_segv 24544
Bus error: 10
...
$ ./bus_segv 28639
Bus error: 10
$ ./bus_segv 28640
...
$ ./bus_segv 45023
$ ./bus_segv 45024
Bus error: 10
...
$ ./bus_segv 53215
Bus error: 10
$ ./bus_segv 53216
...
$ ./bus_segv 69599
$ ./bus_segv 69600
Bus error: 10
...
$ ./bus_segv 73695
Bus error: 10
$ ./bus_segv 73696
...
$ ./bus_segv 77791
$ ./bus_segv 77792
Segmentation fault: 11

如果查看反汇编代码,您会看到带有总线错误的范围的边界不像索引显示的那样奇怪:

$ otool -tv bus_segv

bus_segv:
(__TEXT,__text) section
_main:
0000000100000f60    pushq   %rbp
0000000100000f61    movq    %rsp, %rbp
0000000100000f64    subq    $0x10, %rsp
0000000100000f68    movl    $0x0, -0x4(%rbp)
0000000100000f6f    movl    %edi, -0x8(%rbp)
0000000100000f72    movq    %rsi, -0x10(%rbp)
0000000100000f76    movq    -0x10(%rbp), %rsi
0000000100000f7a    movq    0x8(%rsi), %rdi
0000000100000f7e    callq   0x100000f94 ## symbol stub for: _atol
0000000100000f83    leaq    0x96(%rip), %rsi
0000000100000f8a    movsbl  (%rsi,%rax), %eax
0000000100000f8e    addq    $0x10, %rsp
0000000100000f92    popq    %rbp    
0000000100000f93    retq    

leaq 0x96(%rip), %rsi,rsi成为(PC相对而言) 确定)数组的起始地址:

rsi = 0x100000f8a + 0x96 = 0x100001020
rsi - 4128 = 0x100000000 (below segmentation fault)
rsi + 24544 = 0x100007000 (here and above bus error)
rsi + 28640 = 0x100008000 (below bus error)
rsi + 45024 = 0x10000c000 (here and above bus error)
rsi + 53216 = 0x10000e000 (below bus error)
rsi + 69600 = 0x100012000 (here and above bus error)
rsi + 73696 = 0x100013000 (below bus error)
rsi + 77792 = 0x100014000 (here and above segmentation fault)

lldb可能会设置具有不同页面限制的流程。我无法在调试会话中重现任何总线错误。因此调试器可能是总线错误吐出二进制文件的一种解决方法。

安德烈亚斯