Подтвердить что ты не робот

Может выйти() не завершить процесс?

У меня есть зарегистрированный обработчик сигнала в моей программе. После получения нежелательного сигнала (SIGABRT), я вызываю "exit (-1)" в обработчике сигнала, чтобы выйти из процесса. Но, как замечено в нескольких случаях, он вызывает exit(), но не завершает процесс.

Проблема была случайным образом сгенерирована, и я сильно подозреваю при выполнении exit().

Могут ли быть какие-либо причины или случаи, когда exit() может не завершить процесс.

Спасибо.

4b9b3361

Ответ 1

Вы вызываете exit() из обработчика сигнала?

В man 7 signal, разделе Функции безопасности с использованием Async вы можете увидеть все функции, которые гарантированно будут работать при вызове из обработчика сигнала:

Функция обработчика сигналов должна быть очень осторожной, так как обработка в другом месте может быть прервана на        некоторая произвольная точка в выполнении программы. POSIX имеет понятие "безопасная функция". Если        сигнал прерывает выполнение небезопасной функции, а обработчик вызывает небезопасную функцию, затем        поведение программы undefined.

POSIX.1-2004 (также известный как POSIX.1-2001 Technical Corrigendum 2) требует выполнения        что в обработчике можно безопасно вызвать следующие функции:

Здесь вы можете видеть функции _Exit(), _Exit() и abort(), но особенно не exit(). Поэтому вы не должны вызывать его из обработчика сигналов.

Отвратительно, что даже если вы вызываете небезопасную функцию из обработчика сигнала (printf() any?), он будет работать большую часть времени... но не всегда.

Ответ 2

Да, есть некоторые обстоятельства, например:

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

Если функция, зарегистрированная вызовом atexit(), не возвращается, остальные зарегистрированные функции не будут вызываться, а остальная часть обработки exit() не будет завершена. Если exit() вызывается более одного раза, поведение undefined.

См. страницу POSIX exit.

Для получения дополнительной информации присоедините отладчик, когда вы достигнете ситуации, и посмотрите на стек вызовов.

Ответ 3

У меня была проблема, аналогичная описанной Мадаром. Мне нужно было выполнить действие для каждого сигнала и правильно выйти. Я задумался над парой ответов на похожие вопросы и нашел следующее объяснение/решение.

Объяснение: Одна из проблем заключается в том, что exit() не следует использовать в обработчиках сигналов, поскольку он не является одной из функций, безопасных для асинхронных сигналов (см. man signal-safety). Это означает, что он может, но не гарантированно работать в обработчиках сигналов. В результате вам нужно будет вызвать _exit()/_Exit() (которые безопасны для async-signal). Однако они мгновенно завершают процесс, не вызывая обратные вызовы atexit и статические деструкторы. Насколько я понимаю, для некоторых сигналов можно сделать немного больше очистки, чем обеспечивают эти функции.

Решение: Решение, которое я нашел, состоит в том, чтобы зарегистрировать ваш обработчик сигналов для всех сигналов и выполнить любые дополнительные действия. Затем вы можете сбросить обработчик по умолчанию и вызвать raise(signal_number), которое безопасно для async-signal, чтобы повторно отправить singal и выполнить обработчик по умолчанию.

Вот рабочий пример, который запускает обработчик по умолчанию только на 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;
}