SigHandler вызывает программу не прекратить - PullRequest
0 голосов
/ 15 марта 2019

В настоящее время я пытаюсь создать обработчик сигнала, который при получении сигнала SIGTERM закрывает открытые сетевые сокеты и файловые дескрипторы.

Вот моя функция SigHandler

static void SigHandler(int signo){
    if(signo == SIGTERM){
      log_trace("SIGTERM received - handling signal");
      CloseSockets();
      log_trace("SIGTERM received - All sockets closed");

      if (closeFile() == -1)
        log_trace("SIGTERM received - No File associated with XXX open - continuing with shutdown");
      else
        log_trace("SIGTERM received - Closed File Descriptor for XXX - continuing with shutdown");

      log_trace("Gracefully shutting down XXX Service");
    } else {
      log_trace("%d received - incompatible signal");
      return;
    }

    exit(0);
}

Этот код ниже находится в основном

if (sigemptyset(&set) == SIGEMPTYSET_ERROR){
    log_error("Signal handling initialization failed");
  }
  else {
    if(sigaddset(&set, SIGTERM) == SIGADDSET_ERROR) {
      log_error("Signal SIGTERM not valid");
    }
    action.sa_flags = 0;
    action.sa_mask = set;
    action.sa_handler = &SigHandler;
    if (sigaction(SIGTERM, &action, NULL) == SIGACTION_ERROR) {
      log_error("SIGTERM handler initialization error");
    }
  }

Когда я отправляю kill -15 PID, ничего не происходит. Процесс не завершается, и при этом он не становится процессом зомби (не так, как он должен в любом случае). Однако я вижу печать следов в функции SigHandler, так что я знаю, что она достигает этой точки в коде. Просто кажется, что когда дело доходит до выхода (0), это не работает.

Когда я отправляю SIGKILL (kill -9 PID), он просто отлично убивает процесс.

Извиняюсь, если это расплывчато, я все еще плохо знаком с C, UNIX и т. Д., Так что я совершенно незнаком с большей частью того, как это работает на низком уровне.

Ответы [ 2 ]

4 голосов
/ 15 марта 2019

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

Обычный трюк - установить (в вашем обработчике сигнала) некоторую переменную volatile sig_atomic_t и проверить эту переменную вне обработчика сигнала. Другой возможный трюк - это труба (7) для самообмана (хорошо, документация Qt объясняет it ), когда ваш обработчик сигнала просто выполняет write (2) (который является безопасным для асинхронного сигнала) для некоторого глобального файлового дескриптора , полученного, например, pipe (2) (или, возможно, специфичный для Linux eventfd (2) ...) при инициализации программы перед установкой этого обработчика сигнала.

Особый способ Linux заключается в использовании signalfd (2) для SIGTERM и обработке этого в вашем цикле событий (на основе poll (2) ). Этот трюк концептуально является вариантом трубы для себя. Но у signalfd есть некоторые недостатки, которые вы легко найдете в веб-поиске.

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

Возможно, вы захотите прочитать старую книгу ALP . Там есть несколько хороших объяснений, связанных с вашей проблемой.

PS. Если ваша система QNX, вы должны прочитать ее документацию.

2 голосов
/ 15 марта 2019

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

Также прочитайте (очень внимательно) ответ Basile и внимательно посмотрите на список асинхронных безопасных функций, которые вам разрешено использовать в обработчиках сигналов.

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

...