zoukankan      html  css  js  c++  java
  • 理解SIGBUS与SIGSEGV

    理解SIGBUS与SIGSEGV
    Searched by google


    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

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

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

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

    A: scz 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
    #include

    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
    : save %sp, -128, %sp
    0x10814
    : st %i0, [ %fp + 0x44 ]
    0x10818
    : st %i1, [ %fp + 0x48 ]
    0x1081c
    : sethi %hi(0x12345400), %o1
    0x10820
    : or %o1, 0x278, %o0 ! 0x12345678
    0x10824
    : st %o0, [ %fp + -20 ]
    0x10828
    : clr [ %fp + -24 ]
    0x1082c
    : add %fp, -20, %o0
    0x10830
    : st %o0, [ %fp + -28 ]
    0x10834
    : ld [ %fp + -28 ], %o0
    0x10838
    : clrb [ %o0 ]
    0x1083c
    : ld [ %fp + -28 ], %o0
    0x10840
    : add %o0, 1, %o1
    0x10844
    : st %o1, [ %fp + -24 ]
    0x10848
    : ld [ %fp + -24 ], %o0
    0x1084c
    : clrh [ %o0 ]
    0x10850
    : clr %i0
    0x10854
    : b 0x1085c

    0x10858
    : nop
    0x1085c
    : ret
    0x10860
    : 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进行优化,但仍然使用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
    #include

    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
    : save %sp, -120, %sp
    0x10814
    : st %i0, [ %fp + 0x44 ]
    0x10818
    : st %i1, [ %fp + 0x48 ]
    0x1081c
    : sethi %hi(0x12345400), %o1
    0x10820
    : or %o1, 0x278, %o0 ! 0x12345678
    0x10824
    : st %o0, [ %fp + -20 ]
    0x10828
    : clrh [ %fp + -22 ]
    0x1082c
    : lduh [ %fp + -19 ], %o0
    0x10830
    : sth %o0, [ %fp + -22 ]
    0x10834
    : clr %i0
    0x10838
    : b 0x10840

    0x1083c
    : nop
    0x10840
    : ret
    0x10844
    : restore
    End of assembler dump.
    (gdb) i r pc
    pc 0x1082c 67628
    (gdb)

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

    D: yuhuan@smth.org 2004-01-30 11:48

    参Linux的mmap(2)手册页

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

    SIGSEGV

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

    SIGBUS

    试图访问一块无文件内容对应的内存区域,比如超过文件尾的内存区域,或者以
    前有文件内容对应,现在为另一进程截断过的内存区域。
    --------------------------------------------------------------------------
  • 相关阅读:
    JavaScript正则表达式
    web页面全角&半角
    WEB中的GET和POST
    设计模式之观察者模式
    初识numpy的多维数组对象ndarray
    【ACM】求高精度幂
    C++ 变量初始化规则
    浅谈const限定符
    堆和栈的区别(转过无数次的文章)
    【编程小题目8】求解完数
  • 原文地址:https://www.cnblogs.com/ainima/p/6330840.html
Copyright © 2011-2022 走看看