Могу ли я игнорировать SIGFPE в результате деления на ноль? - PullRequest
4 голосов
/ 07 января 2009

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

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

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

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

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

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

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

Ответы [ 8 ]

6 голосов
/ 07 января 2009

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

Во-вторых, использование SIGFPE в целях прекращения программы явно абсурдно, так как вы должны подавать сигнал, такой как SIGTERM или SIGUSR1, вместо того, чтобы настраивать какой-то другой сигнал для его побочного эффекта и игнорировать его семантическое значение. А точнее, на странице справки для sigaction (2) есть раздел NOTES с рекламой, посвященной 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().

4 голосов
/ 07 января 2009

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

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

2 голосов
/ 07 января 2009

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

signal(SIGFPE, SIG_IGN);

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

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

1 голос
/ 22 мая 2012
void handler(int trapId)
{
   // Whatever
}

signal(SIGFPE, handler);
1 голос
/ 07 января 2009

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

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

#include <fpu_control.h>

_FPU_SETCW (_FPU_DEFAULT);

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

0 голосов
/ 16 февраля 2012

lnmiit l поверх кода сбоя, тогда я бы также рекомендовал изменить код, чтобы он умер другим способом Если вы этого не сделаете, вы можете попробовать установить

0 голосов
/ 07 января 2009

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

0 голосов
/ 07 января 2009

Не могли бы вы заставить программу выйти через менее вымышленное выражение? Например:

#include <signal.h>

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

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

...