Как поймать SIGTERM в многопоточной Linux программе, используя signalfd - PullRequest
0 голосов
/ 22 апреля 2020

Смежный вопрос здесь и здесь .

В проекте Linux GPLv3 + https://github.com/bstarynk/helpcovid/ (многопоточный, C + +17, приложение веб-сервера) commit b616defc5e54ba869 . Необходима возможность изящного завершения такого веб-сервера (взаимодействующего с некоторой базой данных PostGreSQL с использованием libpqxx). Итак, выпуск № 35 .

Я прочитал оба сигнала (7) и Сигнал-безопасность (7) и signalfd ( 2) . И я знаю о критике в отношении signalfd .

У меня есть один фоновый C ++ std::thread poll (2) -ing для нескольких файловых дескрипторов. См. Файл hcv_background.cc, где функция hcv_start_background_thread вызывается из функции main в файле hcv_main.cc, а созданный std::thread запускает функцию hcv_background_thread_body (событие l oop делает опрос (2) ....)

Итак, hcv_start_background_thread имеет:

  {
    sigset_t  sigmaskbits;
    memset (&sigmaskbits, 0, sizeof(sigmaskbits));
    sigemptyset(&sigmaskbits);
    sigaddset(&sigmaskbits, SIGTERM);
    sigaddset(&sigmaskbits, SIGHUP);
    sigaddset(&sigmaskbits, SIGXCPU);
    sigaddset(&sigmaskbits, SIGPIPE);
    /// http://man7.org/linux/man-pages/man2/sigprocmask.2.html
    if (sigprocmask(SIG_UNBLOCK, &sigmaskbits, nullptr))
      HCV_FATALOUT("hcv_start_background_thread: sigprocmask failure");
    HCV_DEBUGOUT("hcv_start_background_thread sigprocmask done");
    hcv_bg_signal_fd = signalfd(-1, &sigmaskbits, SFD_NONBLOCK | SFD_CLOEXEC);
    if (hcv_bg_signal_fd < 0)
      HCV_FATALOUT("hcv_start_background_thread: signalfd failure");
    HCV_DEBUGOUT("hcv_start_background_thread hcv_bg_signal_fd=" << hcv_bg_signal_fd);
  }

, а функция hcv_background_thread_body имеет событие l oop делает

  while (!hcv_should_stop_bg_thread.load())
    {
      struct pollfd polltab[4];
      memset(&polltab, 0, sizeof(polltab));
      polltab[0].fd = hcv_bg_event_fd;
      polltab[0].events = POLL_IN;
      polltab[1].fd = hcv_bg_signal_fd;
      polltab[1].events = POLL_IN;
      polltab[1].fd = hcv_bg_timer_fd;
      polltab[1].events = POLL_IN;
      HCV_DEBUGOUT("hcv_background_thread_body before poll");
      int nbfd = poll(polltab, 3,
                      hcv_debugging.load()?(2*HCV_BACKGROUND_TICK_TIMEOUT):HCV_BACKGROUND_TICK_TIMEOUT);

и позже в том же событии l oop

 if (nbfd>0)   /* some file descriptor is readable */
        {
          HCV_DEBUGOUT("hcv_background_thread_body: after poll nbfd:" << nbfd);
          if ((polltab[0].revents & POLL_IN) && polltab[0].fd == hcv_bg_event_fd)
            {
              int64_t evrk=0;

              HCV_DEBUGOUT("hcv_background_thread_body pollable hcv_bg_event_fd="
                           << hcv_bg_event_fd);
              int byrd = read (hcv_bg_event_fd, &evrk, sizeof(evrk));
              if (byrd==sizeof(evrk))
                {
                  HCV_DEBUGOUT("hcv_background_thread_body: got " << evrk
                               << " from hcv_bg_event_fd=" << hcv_bg_event_fd);
                  hcv_bg_do_event(evrk);
                }
              else
                HCV_SYSLOGOUT(LOG_WARNING,
                              "hcv_background_thread_body read hcv_bg_event_fd#" <<hcv_bg_event_fd << " failed, byrd=" << byrd);
            };
          if ((polltab[1].revents & POLL_IN) && polltab[1].fd == hcv_bg_signal_fd)
            {
              HCV_DEBUGOUT("hcv_background_thread_body pollable hcv_bg_signal_fd="
                           << hcv_bg_signal_fd);
              struct signalfd_siginfo signalinfo;
              memset (&signalinfo, 0, sizeof(signalinfo));
              int byrd = read(hcv_bg_signal_fd, &signalinfo, sizeof(signalinfo));
              if (byrd < 0)
                HCV_FATALOUT("hcv_background_thread_body: failed read of hcv_bg_signal_fd="
                             << hcv_bg_signal_fd);
              else if (byrd != sizeof(signalinfo))
                // should never happen... see signalfd(2)
                HCV_FATALOUT("hcv_background_thread_body: corrupted read of hcv_bg_signal_fd="
                             << hcv_bg_signal_fd << ", byrd=" << byrd);
              HCV_DEBUGOUT("hcv_background_thread_body: got signalinfo #" << signalinfo.ssi_signo
                           << " from hcv_bg_signal_fd=" << hcv_bg_signal_fd);
              if (signalinfo.ssi_signo == SIGTERM)
                {
                  HCV_SYSLOGOUT(LOG_NOTICE, "hcv_background_thread_body got SIGTERM at "
                                << (hcv_monotonic_real_time() - hcv_monotonic_start_time)
                                << " elapsed seconds");
                  hcv_process_SIGTERM_signal();
                  hcv_should_stop_bg_thread.store (true);
                }

Но hcv_process_SIGTERM_signal никогда не вызывали.

Что такое Я делаю не так?

1 Ответ

1 голос
/ 22 апреля 2020

Следующий код:

if (sigprocmask(SIG_UNBLOCK, &sigmaskbits, nullptr))

Должно быть:

if (sigprocmask(SIG_BLOCK, &sigmaskbits, nullptr))

man signalfd:

Обычно набор сигналов, принимаемых через файловый дескриптор, должен быть заблокирован с использованием sigprocmask(2), чтобы предотвратить обработку сигналов в соответствии с их расположением по умолчанию.

Источник программы

...
/* Block signals so that they aren't handled
   according to their default dispositions */
   if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
       handle_error("sigprocmask");
...

Рабочий пример:

#include <iostream>
#include <thread>

#include <sys/signalfd.h>
#include <signal.h>
#include <poll.h>
#include <unistd.h>

void another_thread(int sfd) {
    pollfd fds[2];
    fds[0].fd = STDIN_FILENO;
    fds[0].events = POLLIN;
    fds[1].fd = sfd;
    fds[1].events = POLLIN;

    for(;;) {
        int n = poll(fds, 2, -1);
        if(n <= 0)
            throw;
        if(fds[0].revents & POLLIN) {
            char buf[1024];
            ssize_t r = read(fds[0].fd, buf, sizeof buf);
            if(r > 0)
                std::cout << "Read " << r << " bytes\n";
            else if(!r) {
                std::cout << "Read EOF\n";
                break;
            }
            else
                throw;
        }
        if(fds[1].revents & POLLIN) {
            signalfd_siginfo s;
            ssize_t r = read(fds[1].fd, &s, sizeof s);
            if(r != sizeof s)
                throw;
            std::cout << "Received signal " << s.ssi_signo << '\n';
            break;
        }
    }
}

int main() {
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGTERM);
    sigaddset(&mask, SIGQUIT);
    if(sigprocmask(SIG_BLOCK, &mask, nullptr) == -1)
        throw;

    int sfd = signalfd(-1, &mask, 0);
    if(sfd == -1)
        throw;

    std::thread(another_thread, sfd).join();

    std::cout << "Terminated successfully\n";
}

Вывод:

[max@supernova:~/src/test] $ ./test 
  C-c C-cReceived signal 2
Terminated successfully

Другая ошибка в вашем коде:

      polltab[0].events = POLL_IN;
      polltab[1].fd = hcv_bg_signal_fd;
      polltab[1].events = POLL_IN;
      polltab[1].fd = hcv_bg_timer_fd;
      polltab[1].events = POLL_IN;

hcv_bg_timer_fd, вероятно, следует использовать индекс 2.

...