`
weiyuhu
  • 浏览: 235921 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Program received signal SIGBUS, Bus error

阅读更多
理解SIGBUS与SIGSEGV

Q: SIGSEGV我能理解,但有时碰上SIGBUS,这该如何理解。

A: nkwht@SMTH

nkwht用Google获取这样一些知识。有多种可能导致SIGBUS信号:

1) 硬件故障,不用说,程序员最常碰上的肯定不是这种情形。

2) Linux平台上执行malloc(),如果没有足够的RAM,Linux不是让malloc()失败返回,而是向当前进程分发SIGBUS信号。

注: 对该点执怀疑态度,有机会可自行测试确认当前系统反应。

3) 某些架构上访问数据时有对齐的要求,比如只能从4字节边界上读取一个4字节的数据类型。IA-32架构没有硬性要求对齐,尽管未对齐的访问降低执行效率。另外
一些架构,比如SPARC、m68k,要求对齐访问,否则向当前进程分发SIGBUS信号。

SIGBUS与SIGSEGV信号一样,可以正常捕获。SIGBUS的缺省行为是终止当前进程并产生core dump。

A: Marc Rochkind <rochkind@basepath.com>

SIGBUS与SIGSEGV信号的一般区别如下:

1) SIGBUS(Bus error)意味着指针所对应的地址是有效地址,但总线不能正常使用该指针。通常是未对齐的数据访问所致。

2) SIGSEGV(Segment fault)意味着指针所对应的地址是无效地址,没有物理内存对应该地址。

A: scz <scz@nsfocus.com> 2002-11-20

参"2.4 如何编程获取栈底地址"中如何捕获SIGBUS与SIGSEGV信号,并利用sigsetjmp、siglongjmp重获控制权。

测试表明,在x86/Linux、x86/Solaris、SPARC/Solaris平台上,越过栈底的地址访问导致SIGSEGV信号。在x86 /FreeBSD、x86/NetBSD、x86/OpenBSD平台上,越过栈底的地址访问导致SIGBUS信号,而不是SIGSEGV信号。

下面举例解释一下,什么叫未对齐的数据访问。

--------------------------------------------------------------------------
/*
* Test: SPARC/Solaris 8 64-bit kernel mode
* gcc -Wall -pipe -g -o bus bus.c
*/
#include <stdio.h>
#include <stdlib.h>

int main ( int argc, char * argv[] )
{
unsigned int        i = 0x12345678;
unsigned short int *q = NULL;
unsigned char    *p = ( unsigned char * )&i;

*p = 0x00;
q   = ( unsigned short int * )( p + 1 );
*q = 0x0000;
return( EXIT_SUCCESS );
}   /* end of main */
--------------------------------------------------------------------------

$ ./bus
总线错误 (core dumped)
$ gdb ./bus core
GNU gdb 5.0
#0   0x1084c in main (argc=1, argv=0xffbefc54) at bus.c:16
16       *q = 0x0000;
(gdb) disas main
Dump of assembler code for function main:
0x10810 <main> :    save   %sp, -128, %sp
0x10814 <main+4> :    st   %i0, [ %fp + 0x44 ]
0x10818 <main+8> :    st   %i1, [ %fp + 0x48 ]
0x1081c <main+12>:    sethi   %hi(0x12345400), %o1
0x10820 <main+16>:    or   %o1, 0x278, %o0     ! 0x12345678
0x10824 <main+20>:    st   %o0, [ %fp + -20 ]
0x10828 <main+24>:    clr   [ %fp + -24 ]
0x1082c <main+28>:    add   %fp, -20, %o0
0x10830 <main+32>:    st   %o0, [ %fp + -28 ]
0x10834 <main+36>:    ld   [ %fp + -28 ], %o0
0x10838 <main+40>:    clrb   [ %o0 ]
0x1083c <main+44>:    ld   [ %fp + -28 ], %o0
0x10840 <main+48>:    add   %o0, 1, %o1
0x10844 <main+52>:    st   %o1, [ %fp + -24 ]
0x10848 <main+56>:    ld   [ %fp + -24 ], %o0
0x1084c <main+60>:    clrh   [ %o0 ]
0x10850 <main+64>:    clr   %i0
0x10854 <main+68>:    b   0x1085c <main+76>
0x10858 <main+72>:    nop
0x1085c <main+76>:    ret
0x10860 <main+80>:    restore
End of assembler dump.
(gdb) i r pc
pc          0x1084c   67660
(gdb) i r o0
o0          0xffbefbdd    -4260899
(gdb) x/3bx 0xffbefbdd
0xffbefbdd:     0x34 0x56 0x78
(gdb)

从C语言来说,执行"*q = 0x0000;"时导致SIGBUS了。从汇编指令来说,执行"clrh [%o0]"时导致SIGBUS了,寄存器%o0值为0xffbefbdd,这个地址未对齐在双字节边界上。

注意,gcc编译时并未指定-O<n>进行优化,但仍然使用clrh,而不是两次clrb。类似的汇编指令有ldw、lduh等等。有人可能碰上读操作也导致SIGBUS,觉得不可理解,其实读写导致SIGBUS没有本质区别,比如ldw只能读4字节边界上的地址。

bus.c是显式的未对齐。程序员实际最容易面对的是隐式未对齐,主要来自指针的强制类型转换。下面举例说明这种情形。

--------------------------------------------------------------------------
/*
* Test: SPARC/Solaris 8 64-bit kernel mode
* gcc -Wall -pipe -g -o other_bus other_bus.c
*/
#include <stdio.h>
#include <stdlib.h>

int main ( int argc, char * argv[] )
{
unsigned int        i = 0x12345678;
unsigned short int   j = 0x0000;

j = *( ( unsigned short int * )( ( ( unsigned char * )&i   ) + 1 ) );
return( EXIT_SUCCESS );
}   /* end of main */
--------------------------------------------------------------------------

$ ./other_bus
总线错误 (core dumped)
$ gdb ./other_bus core
GNU gdb 5.0
#0   main (argc=1, argv=0xffbefc44) at other_bus.c:13
13       j = *( ( unsigned short int * )( ( ( unsigned char * )&i   ) + 1 ) );
(gdb) disas main
Dump of assembler code for function main:
0x10810 <main> :    save   %sp, -120, %sp
0x10814 <main+4> :    st   %i0, [ %fp + 0x44 ]
0x10818 <main+8> :    st   %i1, [ %fp + 0x48 ]
0x1081c <main+12>:    sethi   %hi(0x12345400), %o1
0x10820 <main+16>:    or   %o1, 0x278, %o0     ! 0x12345678
0x10824 <main+20>:    st   %o0, [ %fp + -20 ]
0x10828 <main+24>:    clrh   [ %fp + -22 ]
0x1082c <main+28>:    lduh   [ %fp + -19 ], %o0
0x10830 <main+32>:    sth   %o0, [ %fp + -22 ]
0x10834 <main+36>:    clr   %i0
0x10838 <main+40>:    b   0x10840 <main+48>
0x1083c <main+44>:    nop
0x10840 <main+48>:    ret
0x10844 <main+52>:    restore
End of assembler dump.
(gdb) i r pc
pc          0x1082c   67628
(gdb)

因此在SPARC架构上编程,一定要留神强制类型转换,务必清楚自己正在干什么,有没有隐患。

D: yuhuan@SMTH 2004-01-30 11:48

参Linux的mmap(2)手册页

--------------------------------------------------------------------------
使用映射可能涉及到如下信号

SIGSEGV

试图对只读映射区域进行写操作

SIGBUS

试图访问一块无文件内容对应的内存区域,比如超过文件尾的内存区域,或者以前有文件内容对应,现在为另一进程截断过的内存区域
分享到:
评论

相关推荐

    Linux-signal

    - **SIGBUS**:总线错误。 - **SIGSEGV**:段违例,进程试图访问其虚拟地址空间之外的内存。 - **SIGSYS**:系统调用错误。 - **SIGPIPE**:向非读端口写数据。 - **SIGALRM**:定时器信号。 - **SIGTERM**:软件...

    Linux系统编程实验六进程间通信.docx

    `signal()`函数有两个参数:`signum`是信号编号,例如`SIGBUS`表示 Bus Error;`handler`是一个函数指针,用于指定接收信号时执行的函数。实验中定义了一个简单的信号处理函数,当接收到`SIGBUS`信号时,输出一条...

    linux信号机制和signal

    ### Linux信号机制与Signal详解 #### 一、信号机制的基本概念 信号是Linux操作系统中用于进程间通信的重要机制之一,其本质上是一种软中断信号。它主要用于通知进程发生了异步事件,而不涉及具体的数据传递。在...

    Linux 信号signal处理机制.docx

    "Linux 信号signal处理机制" 本文详细介绍了Linux信号机制的基本概念、Linux对信号机制的大致实现方法、如何使用信号,以及有关信号的几个系统调用。信号机制是进程之间相互传递消息的一种方法,信号全称为软中断...

    Linux信号signal处理机制[参考].pdf

    Linux支持多种信号,包括POSIX.1标准定义的信号(如SIGHUP、SIGINT、SIGKILL等)以及一些非标准信号(如SIGBUS、SIGPOLL等)。每种信号都有特定的值和处理动作,比如SIGINT(2)通常代表键盘中断,SIGKILL(9)则是...

    浅析Python中signal包的使用

    在liunx系统中要想每隔一分钟执行一个命令,最普遍的方法就是crontab了,如果不想使用crontab,经同事指点在程序中可以... 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SI

    Linux系统编程实验六:进程间通信.doc

    signal(SIGBUS, sig_handler); ``` 然后向进程发送SIGBUS信号,触发处理函数执行。 3. **消息队列(Message Queue)** 消息队列是另一种IPC机制,允许进程以消息为单位进行数据交换。它提供了更灵活的数据结构和...

    实验五-进程间通信实验(二).doc

    const char data[] = "Pipe Test Program"; int real_read, real_write; memset((void*)buf, 0, sizeof(buf)); /* 创建管道 */ if (pipe(pipe_fd) ) { printf("pipe create error\n"); exit(1); } /* ...

    unix信号和处理方式

    printf("Received signal %d\n", signo); exit(1); } int main() { struct sigaction act; // 初始化sigaction结构体 act.sa_handler = my_signal_handler; act.sa_flags = 0; sigemptyset(&act.sa_mask); ...

    9cc:练习从https://www.sigbus.infocompilerbook制作编译器

    它是一个小型的C语言编译器,由Infocompilerbook的作者在www.sigbus.info上介绍。通过参与9cc的实现,你可以深入理解编译原理,掌握如何将高级语言(如C)转换为机器可执行代码的基本步骤。 编译器是计算机科学中的...

    Android Native Crash分析详解

    2. **SIGBUS(signal 7)**:非法地址访问,但与SIGSEGV不同之处在于访问的地址虽在进程地址空间内,但未按字节对齐(例如,访问未被2或4整除的地址)。另一种情况是在使用mmap映射文件后,文件被截断但仍尝试访问已...

    linux信号的机制

    7. **SIGBUS**:总线错误(Bus Error)信号,当进程尝试访问不正确对齐的内存地址或执行了无效的内存操作时触发。 8. **SIGFPE**:浮点异常(Floating Point Exception)信号,当发生致命的算术运算错误时发出,比如...

    Linux系统编程实验六进程间通信.pdf

    SIGBUS信号通常表示访问了无效内存地址,但在实验中,可能是用来演示信号处理。 4. **消息队列(Message Queue)** 消息队列允许进程之间传递结构化数据,且支持多个进程同时读写。使用`msgget()`创建消息队列,`...

    初步理解Python进程的信号通讯

    在深入了解Python进程的信号通讯之前,首先需要明白什么是信号(signal)。在操作系统层面,信号是一种软件中断的形式,用以实现进程之间的通讯。当一个进程接收到一个信号时,它会暂停当前的程序执行流程,转而处理...

    Linux信号列表[文].pdf

    在Linux操作系统中,信号(Signal)是一种进程间通信机制,用于通知进程发生了特定事件或异常。这份名为"Linux信号列表[文].pdf"的文档详细列出了Linux系统中支持的各种信号,这些信号对于软件开发人员理解和调试...

Global site tag (gtag.js) - Google Analytics