Skip to content

Latest commit

 

History

History
176 lines (141 loc) · 6.46 KB

200307092048.txt.md

File metadata and controls

176 lines (141 loc) · 6.46 KB

2.0 理解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 [email protected]

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

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

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

A: scz [email protected] 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

: 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进行优化,但仍然使用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

: 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: [email protected] 2004-01-30 11:48

参Linux的mmap(2)手册页


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

SIGSEGV

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

SIGBUS

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