Вопрос по c++, c, posix – Портативный способ поймать сигналы и сообщить о проблеме пользователю

4

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

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

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

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

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

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

Ваш Ответ

4   ответа
0

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

1

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

Для bk1e (комментатор на несколько постов вверх) The fact that SIGSEGV is synchronous is why you can't use functions that are not designed to be reentrant. What if the function that crashed was holding a lock, and the function called by the signal handler tries to acquire the same lock?

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

  • asynchronous signal handlers are (generally) hoping to return and resume normal program execution. A handler for a synchronous signal is (generally) going to terminate anyway, so you've not lost much if you crash.
  • in a perverse sense, you have absolute control over when a synchronous signal is delivered - it happens as you execute your defective code, and at no other time. You have no control at all over when an async signal is delivered. Unless the OP's own I/O code is ifself the cause of the defect - e.g. outputting a bad char* - his error message has a reasonable chance of succeeding.
Error: User Rate Limit Exceeded Richard Corden
12

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

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

#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
}

EDIT: Здание на окнах.

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

#include <io.h>
#define STDIO_FILENO 2

EDIT: & APOS; выход & APOS; также не должен вызываться из обработчика сигнала!

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

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

  1. Introduction To Unix Signals Programming
  2. Extending Traditional Signals
Error: User Rate Limit ExceededisError: User Rate Limit Exceeded Richard Corden
Error: User Rate Limit Exceeded
1

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

#ifdef SIGUSR1 /* or whatever */

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

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

Похожие вопросы