Могу ли я игнорировать SIGFPE, полученный в результате деления на ноль?

У меня есть программа, которая намеренно выполняет деление на ноль (и сохраняет результат в изменчивой переменной), чтобы при определенных обстоятельствах останавливаться. Однако я хотел бы иметь возможность отключить эту остановку, не меняя макрос, выполняющий деление на ноль.

Есть ли способ игнорировать это?

я пытался использовать

#include <signal.h>
...
int main(void) {
  signal(SIGFPE, SIG_IGN);
  ...
}

но он все еще умирает с сообщением «Исключение с плавающей запятой (ядро сброшено)».

На самом деле я не использую значение, поэтому мне все равно, что присваивается переменной; 0, случайный, неопределенный...

РЕДАКТИРОВАТЬ: я знаю, что это не самый портативный, но он предназначен для встроенного устройства, которое работает во многих разных ОС. Действие остановки по умолчанию — деление на ноль; другие платформы требуют других приемов для принудительной перезагрузки, вызванной сторожевым таймером (например, бесконечный цикл с отключенными прерываниями). Для тестовой среды ПК (Linux) я хотел отключить остановку при делении на ноль, не полагаясь на такие вещи, как утверждение.


person Mikeage    schedule 07.01.2009    source источник
comment
+1 хороший вопрос. См. комментарии stackoverflow.com/questions/487737. 5 осталось, 10 осталось (1 в день)   -  person VonC    schedule 13.02.2009


Ответы (7)


Почему вы намеренно выполняете деление на ноль, чтобы остановиться? Не могли бы вы exit(-1) или что-то подобное?

Особенно, если вам не нужен результат деления на 0, это просто не имеет смысла.

person lc.    schedule 07.01.2009
comment
Потому что это код, обычно используемый во встроенном устройстве; В большинстве случаев я хочу остановить устройство и перезагрузить сторожевой таймер - person Mikeage; 07.01.2009
comment
есть ли причина, по которой вы не можете отправить SIGQUIT вместо деления на ноль? - person lc.; 07.01.2009
comment
Для этого конкретного теста я просто удалил деление на ноль и использовал другую реализацию. Для встроенного приложения я все еще использую деление на ноль (большинство платформ не POSIX), но там мне не нужно его отключать. - person Mikeage; 07.01.2009

Хорошо, во-первых, вы должны использовать sigaction(2) вместо устаревшего signal().

Во-вторых, использование SIGFPE для завершения программы явно абсурдно, поскольку вы должны генерировать сигнал, такой как SIGTERM или SIGUSR1, вместо того, чтобы фальсифицировать какой-либо другой сигнал из-за его побочного эффекта и игнорировать его семантическую ценность. В частности, на справочной странице для sigaction(2) есть раздел ПРИМЕЧАНИЯ с аннотацией о SIGFPE, который указывает на один или два способа нарушения предполагаемого использования.

Что вам нужно сделать, так это поднять(3) сигнал, когда вы хотите завершить работу. Затем используйте поле sa_handler в структуре sigaction, чтобы изменить, следует ли игнорировать (SIG_IGN) или завершать (SIG_DFL) этот сигнал.

int main(void) {
  struct sigaction my_action;

  my_action.sa_handler = SIG_IGN;
  my_action.sa_flags = SA_RESTART;
  sigaction(SIGUSR1, &my_action, NULL);

  raise(SIGUSR1);  /* Ignored */

  my_action.sa_handler = SIG_DFL;
  sigaction(SIGUSR1, &my_action, NULL);

  raise(SIGUSR1); /* Terminates */

  return 0;
}

Тем не менее, я не понимаю, почему вы должны использовать сигналы вместо простого exit().

person Nietzche-jou    schedule 07.01.2009

Проверьте возвращаемое значение

signal(SIGFPE, SIG_IGN);

Если вы получили SIG_ERR, проверьте значение errno, чтобы узнать, что пошло не так. Это все, что вы можете сделать портативно.

Я знаю, что вы не хотите менять деление на ноль, но это непереносимо и нелогично. Вы хотите создать дамп ядра, если возникнет какое-либо условие, но иметь возможность отключить это поведение без редактирования кода? Используйте assert и NDEBUG.

person fizzer    schedule 07.01.2009

Это не портабельно, но на x86 можно, управляя FPU:

Использование gcc в Linux (я уверен, что другие компиляторы имеют аналогичные возможности):

#include <fpu_control.h>

_FPU_SETCW (_FPU_DEFAULT);

Поскольку _FPU_DEFAULT по умолчанию установлен на _FPU_MASK_ZM (ZM означает Zero-Divide-Mask).

person nimrodm    schedule 07.01.2009

Не могли бы вы заставить программу выйти с помощью менее надуманного выражения? Например:

#include <signal.h>

#ifdef DEBUG
#define DIE_HERE raise(SIGFPE)
#else
#define DIE_HERE
#endif

Я бы не решился переопределить поведение по умолчанию, чтобы какая-то другая часть вашей программы непреднамеренно не делилась на ноль. В качестве альтернативы, если вы заставляете его выйти таким образом, чтобы получить дамп ядра, вы можете изучить возможность использования точек останова в отладчике. Или, если вы работаете в системе с отладчиком gdb, вам может пригодиться утилита gcore.

person Kim Reece    schedule 07.01.2009

Если у вас есть контроль над кодом сбоя, я бы также рекомендовал изменить код, чтобы он умер каким-то другим способом. Если вы этого не сделаете, вы можете попробовать вместо этого установить пустой обработчик сигнала (например, использовать signal(SIGFPE, &EmptyFunction)), но вы все еще полагаетесь на неопределенное поведение, поэтому нет гарантии, что он по-прежнему будет работать в других системах или другом ядре или C. версии библиотеки.

person Doug    schedule 07.01.2009

Если не обращать внимания на сбойный код, я бы также рекомендовал изменить код, чтобы он умирал каким-то другим способом. Если нет, то можно попробовать установить

person requiredbutnevershown    schedule 16.02.2012
comment
Возможно, вы захотите завершить и уточнить свой ответ. В его нынешнем состоянии части, кажется, отсутствуют, и это, по крайней мере, неясно. - person Bart; 16.02.2012