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

时间:2018-08-25 00:05:40

标签: c++ signals stdbind

我正在使用以下代码向我的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);

此代码有效,但需要在与信号标志变量相同的范围内定义信号处理函数。我决定修改代码,并通过引用该函数传递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);

但是,出现此编译错误:

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函数一起使用?

2 个答案:

答案 0 :(得分:2)

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隐式转换为相应的函数指针类型。

答案 1 :(得分:1)

如果您的软件在Linux下运行,并且您的进程执行poll()select(),则使用signalfd()可能会更干净。

目前,我已经在第2789行的snap_communicator 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
}

使用该“套接字”,您可以执行poll(),并在信号到达时触发与读取事件等效的事件。

您可以像这样检索信号信息:

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

其中f_signal_info被声明为:

struct signalfd_siginfo     f_signal_info;

很棒的是,现在所有这些都发生在我的类中,并且我没有任何“怪异”的信号处理程序可以锁定我的线程,不能正确处理C ++异常以及其他潜在问题。最后,我拥有带有虚函数的C ++类,这些虚函数在事件发生时都会被调用,包括Unix信号,例如SIGINTSIGPIPESIGCHLD ...您也可以使用以下方式实现回调增强信号,使您可以使用std::bind()

所有这些都表示,我仍然将signal()用于SIGSEGVSIGBUS等,当它们发生时,我将不做任何额外的工作。我尝试在这些上记录堆栈跟踪,仅此而已。因此,我不会在它们上使用signalfd()