简体   繁体   English

如何在 C++ 中使用 std::bind 函数作为信号处理程序?

[英]How to use std::bind function as a signal handler in C++?

I am using the following code to add signal handling to my C++ classes:我正在使用以下代码将信号处理添加到我的 C++ 类中:

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);

This code works, but it requires the signal handler function to be define in the same scope as the signal flag variable.此代码有效,但它需要在与信号标志变量相同的范围内定义信号处理函数。 I decided to modify the code and pass the signal_flag by reference to the function and use std::bind to "specialize" the handler to my class.我决定修改代码并通过引用函数传递signal_flag并使用std::bind将处理程序“专门化”到我的类。

 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);

However, I get this compile error:但是,我收到此编译错误:

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)’}

Is there a way to use a bound function in conjunction with the signal function in C++?有没有办法将绑定函数与 C++ 中的signal函数结合使用?

The result of std::bind is an unspecified function object whose type cannot be converted into void (*)(int) . std::bind的结果是一个未指定的函数对象,其类型无法转换为void (*)(int) Try encapsulating it:尝试封装它:

void handler_foo(int signal)
{
    return terminate_or_interrupt_handler(signal_flag, signal);
}

Or, if C++11 is available, a lambda might be better:或者,如果 C++11 可用,lambda 可能更好:

signal(SIGTERM, [](int signal) { return terminate_or_interrupt_handler(signal_flag, signal); });

Note that since signal_flag is a global variable (namespace-scope variable), no capture is required.请注意,由于signal_flag是一个全局变量(名称空间范围变量),因此不需要捕获。 A non-capturing lambda can be implicitly converted into the corresponding function pointer type.非捕获 lambda 可以隐式转换为相应的函数指针类型。

If your software runs under Linux and your process does a poll() or a select() it may be a lot cleaner to use signalfd() .如果您的软件在 Linux 下运行并且您的进程执行poll()select() ,那么使用signalfd()可能会更干净。

I've implemented such in my eventdispatcher , the ed::signal class¹.我已经在我的eventdispatcher中实现了这样的功能,即ed::signal类¹。 Here is a copy of the constructor:这是构造函数的副本:

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
}

With that socket you can do a poll() and it triggers an equivalent to a read event when a signal arrives.使用该套接字,您可以执行poll()并在信号到达时触发相当于读取事件的事件。

You retrieve the signal information like this:您可以像这样检索信号信息:

int const r(read(f_socket, &f_signal_info, sizeof(f_signal_info)));

with f_signal_info being declared as: f_signal_info声明为:

struct signalfd_siginfo     f_signal_info;

The great thing is that now all of that happens in my classes and I don't have any weird signal handlers which can get my thread locked up, does not handle C++ exceptions correctly, and other potential problems.很棒的是,现在所有这些都发生在我的类中,而且我没有任何奇怪的信号处理程序可以让我的线程锁定,不能正确处理 C++ 异常,以及其他潜在问题。 On my end, I have C++ classes with virtual functions that get called whenever an event occurs, including Unix signals such as SIGINT , SIGPIPE , SIGCHLD ... You could also implement callbacks using boost signals which give you the power of using std::bind() .在我这边,我有带有虚函数的 C++ 类,只要事件发生就会调用这些虚函数,包括 Unix 信号,例如SIGINTSIGPIPESIGCHLD ...您还可以使用增强信号实现回调,这使您能够使用std::bind()

All of that said, I still use signal() for SIGSEGV , SIGBUS , etc. those that I'm not going to do any extra work when they occur.综上所述,我仍然对SIGSEGVSIGBUS等使用signal() ,当它们发生时我不会做任何额外的工作。 I try to log the stack trace on those and that's it.我尝试在这些上记录堆栈跟踪,仅此而已。 So I don't use signalfd() on them (see my ed::signal_handler implementation).所以我不对它们使用signalfd() (参见我的ed::signal_handler实现)。


¹ The old implementation was part of the snap_communicator environment and the signal class was around line 2789 at time of writing. ¹旧的实现是snap_communicator环境的一部分,在撰写本文时信号类在第 2789 行左右。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM