当输入信号处理程序时,程序计数器(指向当前正在执行的指令的CPU寄存器)被保存在发生被零除的地方.忽略信号会将PC恢复到完全相同的位置,然后再次触发信号(并再次触发).
'x'的值或波动率与此无关 - 零已经转移到CPU寄存器中以准备执行除法.
man 2发出信号说明:
根据POSIX,在忽略不是由kill(2)或raise(3)函数生成的SIGFPE,SIGILL或SIGSEGV信号之后,进程的行为是未定义的.整数除零具有未定义的结果.在某些架构上,它将生成SIGFPE信号.(也将最负整数除以-1可能会生成SIGFPE.)忽略此信号可能会导致无限循环.
如果使用debug标志进行编译,我们可以在gdb中看到这个:
simon@diablo:~$ gcc -g -o sigtest sigtest.c simon@diablo:~$ gdb sigtest GNU gdb 6.8-debian Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i486-linux-gnu"...
默认情况下,gdb不会将SIGINT传递给进程 - 更改此设置以便它看到第一个信号:
(gdb) handle SIGINT pass SIGINT is used by the debugger. Are you sure you want to change it? (y or n) y Signal Stop Print Pass to program Description SIGINT Yes Yes Yes Interrupt
我们走了:
(gdb) run Starting program: /home/simon/sigtest x = 1 Hello World: 1
现在让我们打断它:
^C Program received signal SIGINT, Interrupt. 0xb767e17b in nanosleep () from /lib/libc.so.6
继续分裂:
(gdb) cont Continuing. OUCH! dividing by zero! x = 0 Program received signal SIGFPE, Arithmetic exception. 0x0804853a in main () at sigtest.c:30 30 printf("Hello World: %d\n",1/x);
检查'x'的值,然后继续:
(gdb) print x $1 = 0 (gdb) cont Continuing. FPE! I got a signal: 8 psignal: Floating point exception Program received signal SIGFPE, Arithmetic exception. 0x0804853a in main () at sigtest.c:30 30 printf("Hello World: %d\n",1/x); (gdb) print x $2 = 1
x显然现在是1,我们仍然有一个被零除 - 这是怎么回事?让我们检查底层汇编程序:
(gdb) disassemble Dump of assembler code for function main: 0x080484ca : lea 0x4(%esp),%ecx 0x080484ce : and $0xfffffff0,%esp ... 0x08048533 : mov %eax,%ecx 0x08048535 : mov %edx,%eax 0x08048537 : sar $0x1f,%edx 0x0804853a : idiv %ecx <<-- address FPE occurred at 0x0804853c : mov %eax,0x4(%esp) 0x08048540 : movl $0x8048653,(%esp) 0x08048547 : call 0x8048384 0x0804854c : jmp 0x8048503 End of assembler dump.
之后的一次Google搜索告诉我们,IDIV通过源操作数(ECX)划分EAX寄存器中的值.您可以猜测寄存器内容:
(gdb) info registers eax 0x1 1 ecx 0x0 0 ...