如何在 C++ 中使用 std::bind 函数作为信号处理程序?
How to use std::bind function as a signal handler in C++?
我正在使用以下代码将信号处理添加到我的 C++ classes:
namespace {
std::atomic<bool> signal_flag(false);
}
void terminate_or_interrupt_handler(int signal) {
switch (signal) {
case SIGTERM:
WARN("SIGTERM received");
signal_flag.store(true);
break;
case SIGINT:
WARN("SIGINT received");
signal_flag.store(true);
break;
default:
throw (std::runtime_error("Unhandled signal received"));
}
}
signal(SIGTERM, &terminate_or_interrupt_handler);
此代码有效,但它需要在与信号标志变量相同的范围内定义信号处理函数。我决定修改代码并通过引用函数传递 signal_flag
并使用 std::bind
到 "specialize" 处理程序到我的 class.
void terminate_or_interrupt_handler(std::atomic<bool>& signal_flag, int signal) {
switch (signal) {
case SIGTERM:
WARN("SIGTERM received");
signal_flag.store(true);
break;
case SIGINT:
WARN("SIGINT received");
signal_flag.store(true);
break;
default:
throw (std::runtime_error("Unhandled signal received"));
}
}
auto my_handler = std::bind(terminate_or_interrupt_handler, std::ref(my_class_signal_flag), std::placeholders::_1);
signal(SIGTERM, &my_handler);
但是,我得到这个编译错误:
error: cannot convert ‘std::_Bind<void (*(std::reference_wrapper<std::atomic<bool> >, std::_Placeholder<1>))(std::atomic<bool>&, int)>*’ to ‘__sighandler_t’ {aka ‘void (*)(int)’}
有没有办法将绑定函数与 C++ 中的 signal
函数结合使用?
std::bind
的结果是一个未指定的函数对象,其类型无法转换为void (*)(int)
。尝试封装它:
void handler_foo(int signal)
{
return terminate_or_interrupt_handler(signal_flag, signal);
}
或者,如果 C++11 可用,lambda 可能更好:
signal(SIGTERM, [](int signal) { return terminate_or_interrupt_handler(signal_flag, signal); });
请注意,由于signal_flag
是一个全局变量(名称空间范围变量),因此不需要捕获。非捕获的lambda可以隐式转换为相应的函数指针类型。
如果您的软件在 Linux 下运行并且您的进程执行 poll()
或 select()
,那么使用 signalfd()
.[=29= 可能会更干净]
我已经在我的 eventdispatcher, the ed::signal class¹ 中实现了这样的功能。这是构造函数的副本:
snap_communicator::snap_signal::snap_signal(int posix_signal)
: f_signal(posix_signal)
//, f_socket(-1) -- auto-init
//, f_signal_info() -- auto-init
//, f_unblock(false) -- auto-init
{
int const r(sigismember(&g_signal_handlers, f_signal));
if(r != 0)
{
if(r == 1)
{
// this could be fixed, but probably not worth the trouble...
throw snap_communicator_initialization_error("the same signal cannot be created more than once in your entire process.");
}
// f_signal is not considered valid by this OS
throw snap_communicator_initialization_error("posix_signal (f_signal) is not a valid/recognized signal number.");
}
// create a mask for that signal
//
sigset_t set;
sigemptyset(&set);
sigaddset(&set, f_signal); // ignore error, we already know f_signal is valid
// first we block the signal
//
if(sigprocmask(SIG_BLOCK, &set, nullptr) != 0)
{
throw snap_communicator_runtime_error("sigprocmask() failed to block signal.");
}
// second we create a "socket" for the signal (really it is a file
// descriptor manager by the kernel)
//
f_socket = signalfd(-1, &set, SFD_NONBLOCK | SFD_CLOEXEC);
if(f_socket == -1)
{
int const e(errno);
SNAP_LOG_ERROR("signalfd() failed to create a signal listener for signal ")(f_signal)(" (errno: ")(e)(" -- ")(strerror(e))(")");
throw snap_communicator_runtime_error("signalfd() failed to create a signal listener.");
}
// mark this signal as in use
//
sigaddset(&g_signal_handlers, f_signal); // ignore error, we already know f_signal is valid
}
使用那个 socket 你可以做一个 poll()
并且当信号到达时它会触发一个相当于读取事件的事件。
你这样检索信号信息:
int const r(read(f_socket, &f_signal_info, sizeof(f_signal_info)));
f_signal_info
声明为:
struct signalfd_siginfo f_signal_info;
很棒的是,现在所有这些都发生在我的 classes 中,而且我没有任何 weird 信号处理程序可以锁定我的线程,没有正确处理 C++ 异常,以及其他潜在问题。在我这边,我有 C++ classes 和虚函数,只要事件发生就会调用它们,包括 Unix 信号,例如 SIGINT
、SIGPIPE
、SIGCHLD
...还可以使用增强信号实现回调,这使您能够使用 std::bind()
.
综上所述,我仍然将 signal()
用于 SIGSEGV
、SIGBUS
等,当它们出现时我不会做任何额外的工作。我尝试在这些上记录堆栈跟踪,仅此而已。所以我不对它们使用 signalfd()
(参见我的 ed::signal_handler 实现)。
¹ 旧实现是 snap_communicator 环境的一部分,信号 class 在撰写本文时位于第 2789 行附近。
我正在使用以下代码将信号处理添加到我的 C++ classes:
namespace {
std::atomic<bool> signal_flag(false);
}
void terminate_or_interrupt_handler(int signal) {
switch (signal) {
case SIGTERM:
WARN("SIGTERM received");
signal_flag.store(true);
break;
case SIGINT:
WARN("SIGINT received");
signal_flag.store(true);
break;
default:
throw (std::runtime_error("Unhandled signal received"));
}
}
signal(SIGTERM, &terminate_or_interrupt_handler);
此代码有效,但它需要在与信号标志变量相同的范围内定义信号处理函数。我决定修改代码并通过引用函数传递 signal_flag
并使用 std::bind
到 "specialize" 处理程序到我的 class.
void terminate_or_interrupt_handler(std::atomic<bool>& signal_flag, int signal) {
switch (signal) {
case SIGTERM:
WARN("SIGTERM received");
signal_flag.store(true);
break;
case SIGINT:
WARN("SIGINT received");
signal_flag.store(true);
break;
default:
throw (std::runtime_error("Unhandled signal received"));
}
}
auto my_handler = std::bind(terminate_or_interrupt_handler, std::ref(my_class_signal_flag), std::placeholders::_1);
signal(SIGTERM, &my_handler);
但是,我得到这个编译错误:
error: cannot convert ‘std::_Bind<void (*(std::reference_wrapper<std::atomic<bool> >, std::_Placeholder<1>))(std::atomic<bool>&, int)>*’ to ‘__sighandler_t’ {aka ‘void (*)(int)’}
有没有办法将绑定函数与 C++ 中的 signal
函数结合使用?
std::bind
的结果是一个未指定的函数对象,其类型无法转换为void (*)(int)
。尝试封装它:
void handler_foo(int signal)
{
return terminate_or_interrupt_handler(signal_flag, signal);
}
或者,如果 C++11 可用,lambda 可能更好:
signal(SIGTERM, [](int signal) { return terminate_or_interrupt_handler(signal_flag, signal); });
请注意,由于signal_flag
是一个全局变量(名称空间范围变量),因此不需要捕获。非捕获的lambda可以隐式转换为相应的函数指针类型。
如果您的软件在 Linux 下运行并且您的进程执行 poll()
或 select()
,那么使用 signalfd()
.[=29= 可能会更干净]
我已经在我的 eventdispatcher, the ed::signal class¹ 中实现了这样的功能。这是构造函数的副本:
snap_communicator::snap_signal::snap_signal(int posix_signal)
: f_signal(posix_signal)
//, f_socket(-1) -- auto-init
//, f_signal_info() -- auto-init
//, f_unblock(false) -- auto-init
{
int const r(sigismember(&g_signal_handlers, f_signal));
if(r != 0)
{
if(r == 1)
{
// this could be fixed, but probably not worth the trouble...
throw snap_communicator_initialization_error("the same signal cannot be created more than once in your entire process.");
}
// f_signal is not considered valid by this OS
throw snap_communicator_initialization_error("posix_signal (f_signal) is not a valid/recognized signal number.");
}
// create a mask for that signal
//
sigset_t set;
sigemptyset(&set);
sigaddset(&set, f_signal); // ignore error, we already know f_signal is valid
// first we block the signal
//
if(sigprocmask(SIG_BLOCK, &set, nullptr) != 0)
{
throw snap_communicator_runtime_error("sigprocmask() failed to block signal.");
}
// second we create a "socket" for the signal (really it is a file
// descriptor manager by the kernel)
//
f_socket = signalfd(-1, &set, SFD_NONBLOCK | SFD_CLOEXEC);
if(f_socket == -1)
{
int const e(errno);
SNAP_LOG_ERROR("signalfd() failed to create a signal listener for signal ")(f_signal)(" (errno: ")(e)(" -- ")(strerror(e))(")");
throw snap_communicator_runtime_error("signalfd() failed to create a signal listener.");
}
// mark this signal as in use
//
sigaddset(&g_signal_handlers, f_signal); // ignore error, we already know f_signal is valid
}
使用那个 socket 你可以做一个 poll()
并且当信号到达时它会触发一个相当于读取事件的事件。
你这样检索信号信息:
int const r(read(f_socket, &f_signal_info, sizeof(f_signal_info)));
f_signal_info
声明为:
struct signalfd_siginfo f_signal_info;
很棒的是,现在所有这些都发生在我的 classes 中,而且我没有任何 weird 信号处理程序可以锁定我的线程,没有正确处理 C++ 异常,以及其他潜在问题。在我这边,我有 C++ classes 和虚函数,只要事件发生就会调用它们,包括 Unix 信号,例如 SIGINT
、SIGPIPE
、SIGCHLD
...还可以使用增强信号实现回调,这使您能够使用 std::bind()
.
综上所述,我仍然将 signal()
用于 SIGSEGV
、SIGBUS
等,当它们出现时我不会做任何额外的工作。我尝试在这些上记录堆栈跟踪,仅此而已。所以我不对它们使用 signalfd()
(参见我的 ed::signal_handler 实现)。
¹ 旧实现是 snap_communicator 环境的一部分,信号 class 在撰写本文时位于第 2789 行附近。