如何使用 signalfd 在多线程 Linux 程序中捕获 SIGTERM
How to catch SIGTERM in a multi threaded Linux program using signalfd
相关问题here and here。
在 Linux GPLv3+ 项目中 https://github.com/bstarynk/helpcovid/ (multi-threaded, C++17, web server application) commit b616defc5e54ba869. The need is to be able to terminate gracefully such a web server (interacting with some PostGreSQL database using libpqxx
). So issue#35。
我都读了signal(7) and signal-safety(7) and signalfd(2). And I am aware of criticisms against signalfd。
我有一个单一的背景 C++ std::thread
poll(2)-ing on several file descriptors. See file hcv_background.cc
where the hcv_start_background_thread
function is called from main
function in file hcv_main.cc
and the created std::thread
runs the hcv_background_thread_body
function (an event loop doing the poll(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
函数有一个事件循环做
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);
之后在同一事件循环中
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
从来没有接到电话。
我做错了什么?
以下代码:
if (sigprocmask(SIG_UNBLOCK, &sigmaskbits, nullptr))
应该是:
if (sigprocmask(SIG_BLOCK, &sigmaskbits, nullptr))
Normally, the set of signals to be received via the
file descriptor should be blocked using sigprocmask(2)
, to prevent
the signals being handled according to their default dispositions.
Program source
...
/* 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。
相关问题here and here。
在 Linux GPLv3+ 项目中 https://github.com/bstarynk/helpcovid/ (multi-threaded, C++17, web server application) commit b616defc5e54ba869. The need is to be able to terminate gracefully such a web server (interacting with some PostGreSQL database using libpqxx
). So issue#35。
我都读了signal(7) and signal-safety(7) and signalfd(2). And I am aware of criticisms against signalfd。
我有一个单一的背景 C++ std::thread
poll(2)-ing on several file descriptors. See file hcv_background.cc
where the hcv_start_background_thread
function is called from main
function in file hcv_main.cc
and the created std::thread
runs the hcv_background_thread_body
function (an event loop doing the poll(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
函数有一个事件循环做
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);
之后在同一事件循环中
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
从来没有接到电话。
我做错了什么?
以下代码:
if (sigprocmask(SIG_UNBLOCK, &sigmaskbits, nullptr))
应该是:
if (sigprocmask(SIG_BLOCK, &sigmaskbits, nullptr))
Normally, the set of signals to be received via the file descriptor should be blocked using
sigprocmask(2)
, to prevent the signals being handled according to their default dispositions.Program source
...
/* 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。