2015-11-07 2 views
0

Я только что сообщил myselve о «сигналах» на C/C++ и играл вокруг. Но у меня есть проблема, чтобы понять логику SIGFPE.Как использовать SIGFPE с сигналом?

Я написал небольшую программу, которая будет работать в деление на ноль, если это произойдет, тогда сигнал должен быть запущен, и обработчик сигнала должен быть выполнен. Но вместо этого моя программа просто падает. Итак, какова цель SIGFPE, если она даже не работает при делении на ноль?

#include <stdio.h> 
#include <signal.h> 
#include <iostream> 

int signal_status = 0; 

void my_handler (int param) 
{ 
    signal_status = 1; 
    printf ("DIVISION BY ZERO!"); 
} 

int main() 
{ 

    signal (SIGFPE, my_handler); 
    int result = 0; 

    while(1) 
    { 
     system("cls"); 
     printf ("signaled is %d.\n", signal_status); 

     for(int i=10000; i>-1; i--) 
     { 
      result = 5000/i; 
     } 
    } 

    getchar(); 

    return 0; 
} 
+1

Большинство сигналов зависит от ОС. Для Linux внимательно прочитайте [signal (7)] (http://man7.org/linux/man-pages/man7/signal.7.html) и обратите внимание, что вам разрешено использовать только безопасные функции асинхронного сигнала (есть очень немногие из них) в обработчиках сигналов. В частности, использование 'printf' (или' std :: cout' и, например, его 'operator <<') в обработчике сигнала запрещено –

+0

Возможная дубликация [Сигнал SIGFPE вопрос] (https://stackoverflow.com/questions/6628947/signal-sigfpe-question) –

ответ

1

Как я уже говорил, большинство сигналов зависит от ОС. Для Linux внимательно прочитайте signal(7). Вы забыли \n внутри своего printf (обычно вам будет повезло увидеть что-то в вашем коде, но прочитайте весь мой ответ). И в принципе вы не должны называть printf (который не является функцией защиты от асинхронного сигнала, вы должны использовать напрямую и только write(2) внутри) от вашего обработчика сигнала.

Что, вероятно, происходит это (не обращая внимания на undefined behavior, создаваемый неправильно используя printf внутри обработчик сигналу) является то, что:

  • вашего stdout буфер никогда не покраснел, так как вы забыли \n (вы можете добавить fflush(NULL); ...) в printf внутри my_handler в коде

  • вероятно, обработчик SIGFPE снова перезагружает machine code инструкция запуска его. (Более точно, после возвращения из sigreturn(2) ваша машина находится в том же состоянии, как это было раньше SIGFPE было доставлено, так же условие деления на ноль происходит, и т.д ...)

трудно (но с болью возможно, если вы согласны с кодированием аппаратного и специфического для операционной системы кода) для обработки SIGFPE; вы бы использовать sigaction(2) с SA_SIGINFO и обрабатывать третий аргумент обработчика сигнала (который является ucontext_t указатель косвенно давая состояние машины, в том числе processor registers, которые вы можете изменить внутри обработчика, в частности, вы могли бы изменить ваше возвращение program counter там) , Вы можете также рассмотреть возможность использования sigsetjmp(3) внутри вашего обработчика сигнала (но это теоретически запрещено, поскольку не безопасно для асинхронного сигнала).

(Вы, конечно, нужно понимать детали instruction set архитектуры вашего процессора и вашей операционной системы ABI, и вы, вероятно, потребуется неделя кодирования работы после того, освоив их)

В портативном POSIX пути, SIGFPE не может действительно быть обработаны, как описано в Blue Moon's answer

Вероятно, время выполнения JVM или SBCL обрабатывает SIGFPE в машине & операционной системы определенным образом сообщать нулевое деление как исключение по принципу «деление на ноль» .... (для программ Java для JVM, для общих программ Lisp для SBCL). В качестве альтернативы их JIT или компилятор могут генерировать тест перед каждым подразделением.

BTW, флаг, установленный внутри обработчика сигнала, должен быть объявлен volatile sig_atomic_t.См POSIX спецификации о <signal.h>

Как прагматическом правило, обработчик портативный и надежный сигнал POSIX должен только установить некоторые volatile sig_atomic_t и/или, возможно, write(2) несколько байт в какой-то pipe(7) (ваш процесс мог бы создать трубу к себе - а recommended by Qt - с другой нитью и/или какой-то цикл обработки событий чтения), но это не работает для асинхронных process -порожденных сигналы, как SIGFPE, SIGBUS, SIGILL и SIGSEGV, и т.д. ... (который может быть обработан только с помощью болезненного компьютерного кода).

См. Также this answer к очень близкому вопросу.

Наконец, в Linux обработка сигналов считается не очень быстрой. Даже с большим количеством машинного кодирования, эмулируя GNU Hurd external pagers хитростью SIGSEGV, обработка (которая была бы mmap lazily ....), как полагают, была довольно медленной.

2

Деление на ноль - undefined behaviour. Поэтому, если вы установили обработчик для SIGFPE или нет, это не имеет большого значения, когда ваша программа вызывает неопределенное поведение.

POSIX говорит:

Доставка сигнала не будет иметь никакого влияния на процесс. Поведение процесса не определено после того, как оно игнорирует сигнал SIGFPE, SIGILL, SIGSEGV или SIGBUS, который не был создан kill(), sigqueue() или raise().

сигнала возникают в результате какого-либо события (например, отправка SIGINT нажатия CTRL + C), который может быть обработан с помощью процесса , если упомянутого события нефатального. SIGFPE - ошибочное условие в программе, и вы не можете ручкой что. Аналогичным случаем будет попытка обработать SIGSEGV, что эквивалентно этому (неопределенное поведение). Когда ваш процесс пытается получить доступ к некоторой памяти, для которой у нее нет доступа. Было бы глупо, если бы вы могли просто игнорировать его и продолжать, как будто ничего не произошло.

+0

Хорошо, но на сайте, где я нашел описанную сигнальную функцию, есть следующее объяснение о 'SIGFPE' « Сигнал отображается на делении на 0 »(я перевел его с немецкого) Но, как мы видим, нет смысла использовать 'SIGFPE'. – Black

+1

@EdwardBlack: вы * можете * обрабатывать 'SIGFPE', но вам нужен сложный, операционный системный и аппаратный код. См. Мой ответ. –

Смежные вопросы