Может ли exit () прервать процесс? - PullRequest
11 голосов
/ 12 января 2012

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

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

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

Спасибо.

Ответы [ 3 ]

15 голосов
/ 12 января 2012

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

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

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

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

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

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

3 голосов
/ 12 января 2012

Да, существуют некоторые обстоятельства, такие как:

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

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

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

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

1 голос
/ 11 января 2019

У меня была проблема, аналогичная описанной 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;
}
...