Портативный способ поймать сигналы и сообщить о проблеме пользователю - PullRequest
4 голосов
/ 19 сентября 2008

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

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

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

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

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

РЕДАКТИРОВАТЬ: Или хотя бы переносимым в рамках POSIX?

Ответы [ 4 ]

12 голосов
/ 19 сентября 2008

В этой таблице перечислены все функции, которые 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
}

РЕДАКТИРОВАТЬ: Здание на окнах.

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

#include <io.h>
#define STDIO_FILENO 2

РЕДАКТИРОВАТЬ: 'обработчик' также не должен вызываться из обработчика сигнала!

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

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

  1. Введение в программирование сигналов Unix
  2. Расширение традиционных сигналов
1 голос
/ 22 сентября 2008

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

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

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

  • обработчики асинхронного сигнала (в общем) в надежде вернуться и возобновить нормальное выполнение программы. Обработчик для синхронного сигнала (вообще) собирается прекратить во всяком случае, так что вы не много потеряли, если ты разбился.
  • в извращенном смысле вы имеете абсолютный контроль над доставкой синхронного сигнала - это происходит при выполнении вашего дефектного кода, и ни в какое другое время. Вы вообще не можете контролировать передачу асинхронного сигнала. Если только собственный код ввода-вывода OP не является причиной неисправности, например вывод плохого символа * - его сообщение об ошибке имеет разумный шанс на успех.
1 голос
/ 19 сентября 2008

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

#ifdef SIGUSR1 /* or whatever */

и т. Д. Вокруг всех ссылок на сигналы, которые не гарантируются стандартом C.

Также, как отмечалось выше, вы не хотите обрабатывать SIGUSR1, SIGHUP, SIGINT, SIGQUIT и SIGTERM, как это.

0 голосов
/ 19 сентября 2008

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

...