У меня была проблема, аналогичная описанной Madar .Мне нужно было выполнить действие для каждого сигнала и правильно выйти.Я спросил несколько ответов на похожие проблемы и нашел следующее объяснение / решение:
Объяснение: Одна проблема заключается в том, что exit()
не следует использовать в обработчиках сигналов, потому что этоне является одной из безопасных для асинхронного сигнала функций (см. man signal-safety
).Это означает, что он может, но не гарантированно работать в обработчиках сигналов.В результате вам нужно будет позвонить _exit()
/ _Exit()
(которые безопасны для асинхронных сигналов).Однако они мгновенно завершают процесс, не вызывая обратные вызовы atexit
и статические деструкторы.Насколько я понимаю, для некоторых сигналов можно сделать немного больше очистки, чем обеспечивают эти функции.
Решение: Решение, которое я нашел, состоит в том, чтобы зарегистрировать ваш обработчик сигналов для всех сигналов и выполнить любые дополнительные действия.Затем вы можете сбросить обработчик по умолчанию и вызвать raise(signal_number)
, который безопасен по асинхронному сигналу, чтобы повторно отправить сигнал и выполнить обработчик по умолчанию.
Вот рабочий пример, который запускает обработчик по умолчанию только на SIGINT
.Я думаю, что это слишком просто, чтобы испытать «сбой» exit()
, если вы использовали его в обработчике.Я протестировал подобный код с альтернативным стеком, который также обрабатывает SIGSEGV
.
Примечание Если вы хотите, чтобы это работало правильно в многопоточном контексте (например, несколько потоков, вызывающих SIGSEGV
вв то же время) вам нужно позаботиться о синхронизации.Потоки используют один и тот же обработчик, но имеют отдельную маскировку сигналов.
#include <csignal>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <unistd.h>
// The actual signal handler
extern "C" void handleSignal(int sig, siginfo_t *siginfo, void *) {
// Cannot use printf() - not async-signal-safe
// For simplicity I use a single call to write here
// though it is not guaranteed to write the whole message
// You need to wrap it in a loop
// Die only on Ctrl+C
if(sig == SIGINT) {
const char *msg = "Die\n";
write(STDERR_FILENO, msg, ::strlen(msg));
// Reset to use the default handler to do proper clean-up
// If you want to call the default handler for every singal
// You can avoid the call below by adding SA_RESETHAND to sa_flags
signal(sig, SIG_DFL);
raise(sig);
return;
}
// Here we want to handle the signal ourselves
// We have all the info available
const char *msg = "Continue\n";
write(STDERR_FILENO, msg, ::strlen(msg));
}
int main() {
// You might want to setup your own alternative stack
// eg. to handle SIGSEGV correctly
// sigaltstack() + SA_ONSTACK flag in sa_flag
// Prepare a signal action for handling any signal
struct sigaction signal_action;
signal_action.sa_sigaction = ::handleSignal;
signal_action.sa_flags = SA_SIGINFO;
::sigfillset(&signal_action.sa_mask);
// A vector of all signals that lead to process termination by default
// (see man -s 7 signal)
const int TERM_SIGNALS[] = {
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV,
SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2, SIGBUS, SIGPOLL,
SIGPROF, SIGSYS, SIGTRAP, SIGVTALRM, SIGXCPU, SIGXFSZ};
// Register the signal event handler for every terminating signal
for (auto sig : TERM_SIGNALS) {
::sigaction(sig, &signal_action, 0);
}
while(true);
return 0;
}