Когда вводится обработчик сигнала, счетчик программы (регистр ЦП, указывающий на текущую выполняемую инструкцию) сохраняется там, где произошло деление на ноль.Игнорирование сигнала восстанавливает ПК точно в том же месте, на котором сигнал запускается снова (и снова, и снова).
Значение или изменчивость 'x' не имеет значения в этом пункте - ноль имеетбыл переведен в регистр ЦП в готовности выполнить деление.
сигнал man 2 отмечает, что:
Согласно POSIX, поведение процессане определено после того, как игнорируется сигнал SIGFPE, SIGILL или SIGSEGV, который не был сгенерирован функциями kill (2) или Повышение (3).Целочисленное деление на ноль имеет неопределенный результат.На некоторых архитектурах он генерирует сигнал SIGFPE.(Также деление самого отрицательного целого числа на -1 может генерировать SIGFPE.) Игнорирование этого сигнала может привести к бесконечному циклу.
Мы можем увидеть это в 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.
<b>0x0804853a</b> 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.
<b>0x0804853a</b> 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
<b>0x0804853a : idiv %ecx <<-- address FPE occurred at</b>
0x0804853c : mov %eax,0x4(%esp)
0x08048540 : movl $0x8048653,(%esp)
0x08048547 : call 0x8048384
0x0804854c : jmp 0x8048503
End of assembler dump.
Один поиск в Google позже скажет, что IDIV делит значение в регистре EAX на операнд-источник (ECX).Вы, вероятно, можете угадать содержимое регистра:
(gdb) info registers
eax 0x1 1
ecx 0x0 0
...