Портативный способ улавливать сигналы и сообщать пользователю о проблеме

StackOverflow https://stackoverflow.com/questions/103280

Вопрос

Если каким-то чудом в нашей программе произойдет ошибка segfault, я хочу перехватить SIGSEGV и сообщить пользователю (возможно, клиенту с графическим интерфейсом) с помощью одного кода возврата, что произошла серьезная проблема.В то же время я хотел бы отобразить информацию в командной строке, чтобы показать, какой сигнал был перехвачен.

Сегодня наш обработчик сигналов выглядит следующим образом:

void catchSignal (int reason) {
  std :: cerr << "Caught a signal: " << reason << std::endl;
  exit (1);
}

Я могу слышать крики ужаса из-за вышесказанного, поскольку я прочитал из этого Нитки что вызывать нереентерабельную функцию из обработчика сигнала - это зло.

Существует ли портативный способ обработки сигнала и предоставления информации пользователям?

Редактировать: Или, по крайней мере, переносимый в рамках POSIX?

Это было полезно?

Решение

Это таблица перечисляет все функции, которые POSIX гарантирует безопасность асинхронных сигналов и поэтому могут быть вызваны из обработчика сигналов.

Используя команду 'write' из этой таблицы, мы надеемся, что следующее относительно "уродливое" решение сделает свое дело:

#include <csignal>

#ifdef _WINDOWS_
#define _exit _Exit
#else
#include <unistd.h>
#endif

#define PRINT_SIGNAL(X) case X: \
          write (STDERR_FILENO, #X ")\n" , sizeof(#X ")\n")-1); \
          break;

void catchSignal (int reason) {
  char s[] = "Caught signal: (";
  write (STDERR_FILENO, s, sizeof(s) - 1);
  switch (reason)
  {
    // These are the handlers that we catch
    PRINT_SIGNAL(SIGUSR1);
    PRINT_SIGNAL(SIGHUP);
    PRINT_SIGNAL(SIGINT);
    PRINT_SIGNAL(SIGQUIT);
    PRINT_SIGNAL(SIGABRT);
    PRINT_SIGNAL(SIGILL);
    PRINT_SIGNAL(SIGFPE);
    PRINT_SIGNAL(SIGBUS);
    PRINT_SIGNAL(SIGSEGV);
    PRINT_SIGNAL(SIGTERM);
  }

  _Exit (1);  // 'exit' is not async-signal-safe
}

Редактировать: Здание на окнах.

После попытки собрать эту Windows, оказывается, что 'STDERR_FILENO' не определен.Однако из документации следует, что его значение равно "2".

#include <io.h>
#define STDIO_FILENO 2

Редактировать: 'exit' также не должен вызываться из обработчика сигнала!

Как было указано шипучка, вызов _Exit в приведенном выше примере является методом "кувалды" для таких сигналов, как HUP и TERM.В идеале, когда эти сигналы перехватываются, флаг с типом "volatile sig_atomic_t" может быть использован для уведомления основной программы о том, что она должна выйти.

Следующее, что я нашел полезным в своих поисках.

  1. Введение В Программирование сигналов Unix
  2. Расширение традиционных сигналов

Другие советы

FWIW, 2 также является стандартной ошибкой в Windows, но вам понадобится некоторая условная компиляция, потому что их функция write() называется _write() .Вы также захотите

#ifdef SIGUSR1 /* or whatever */

etc вокруг всех ссылок на сигналы, которые гарантированно не будут определены стандартом C.

Кроме того, как отмечалось выше, вы не хотите обрабатывать SIGUSR1, SIGHUP, SIGINT, SIGQUIT и SIGTERM подобным образом.

Ричард, все еще недостаточно кармы, чтобы комментировать, поэтому, боюсь, новый ответ.Это асинхронные сигналы;вы понятия не имеете, когда они будут доставлены, так что, возможно, вы окажетесь в библиотечном коде, который необходимо завершить, чтобы оставаться согласованным.Поэтому для возврата этих сигналов требуются обработчики сигналов.Если вы вызовете exit(), библиотека выполнит некоторую работу после main(), включая вызов функций, зарегистрированных в atexit(), и очистку стандартных потоков.Эта обработка может завершиться неудачей, если, скажем, ваш сигнал поступил в стандартную библиотечную функцию ввода-вывода.Поэтому в C90 вам не разрешено вызывать exit().Теперь я вижу, что C99 смягчает требование, предоставляя новую функцию _Exit() в stdlib.h._Exit() может быть безопасно вызван из обработчика для асинхронного сигнала._Exit() не будет вызывать функции atexit() и может не выполнять очистку стандартных потоков по усмотрению реализации.

К bk1e (прокомментируйте несколько постов выше) Тот факт, что SIGSEGV является синхронным, объясняет, почему вы не можете использовать функции, которые не предназначены для повторного входа.Что, если функция, которая завершилась сбоем, удерживала блокировку, и функция, вызванная обработчиком сигнала, пытается получить ту же блокировку?

Это возможно, но проблема не в "том факте, что SIGSEGV является синхронным".Вызов нереентерабельных функций из обработчика намного хуже с асинхронными сигналами по двум причинам:

  • асинхронные обработчики сигналов (как правило) надеются вернуться и возобновить нормальное выполнение программы. Обработчик синхронного сигнала (как правило) завершается в любом случае, так что вы мало что потеряете, если произойдет сбой.
  • в извращенном смысле у вас есть абсолютный контроль над тем, когда подается синхронный сигнал - это происходит, когда вы выполняете свой дефектный код, и ни в какое другое время.У вас вообще нет никакого контроля над тем, когда подается асинхронный сигнал.Если только собственный код ввода-вывода операционной системы не является ifself причиной дефекта - напримервывод неверного символа * - его сообщение об ошибке имеет разумные шансы на успех.

Напишите программу запуска, которая запустит вашу программу и сообщит пользователю об аномальном коде выхода.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top