4

在 C++11 中,给定一种请求循环(作为 Web 请求循环的一部分),在捕获的信号上执行不安全代码的最安全(并且最好是最有效)的方法是什么?例如,在从 linux 命令行捕获 SIGUSR1 时:kill -30 <process pid>

在下一个触发请求时运行“不安全代码”是可以接受的,如果在运行不安全代码之前多次触发信号,则不会丢失任何信息。

例如,我当前的代码是:

static bool globalFlag = false;

void signalHandler(int sig_num, siginfo_t * info, void * context) {
    globalFlag = true;
}
void doUnsafeThings() {
    // thigns like std::vector push_back, new char[1024], set global vars, etc.
}
void doRegularThings() {
    // read filesystem, read global variables, etc.
}

void main(void) {

    // set up signal handler (for SIGUSR1) ...
    struct sigaction sigact;
    sigact.sa_sigaction = onSyncSignal;
    sigact.sa_flags = SA_RESTART | SA_SIGINFO;
    sigaction(SIGUSR1, &sigact, (struct sigaction *)NULL);

    // main loop ...
    while(acceptMoreRequests()) { // blocks until new request received
        if (globalFlag) {
            globalFlag = false;
            doUnsafeThings();
        }
        doRegularThings();
    }
}

我知道在主循环测试+设置 globalFlag 布尔值时可能存在问题。

编辑:if (globalFlag)测试将在一个相当紧凑的循环中运行,“偶尔”的假阴性是可以接受的。但是,我怀疑 Basile Starynkevitch 的解决方案没有任何优化?

4

1 回答 1

9

你应该声明你的旗帜

  static volatile sig_atomic_t globalFlag = 0;

请参阅例如sig_atomic_t这个问题 ,不要忘记volatile限定词。(它可能拼写sigatomic_t为 C)。

在 Linux(特别是)上,您可以使用signalfd(2)来获取信号的文件描述符,并且该 fd 可以由您的事件循环进行poll(2)编辑。

一些事件循环库(libeventlibev ...)知道如何处理信号。

还有一个技巧是在初始化时设置一个管道(参见pipe(2)pipe(7)了解更多信息),然后在信号处理程序中写入一些字节。事件循环将轮询并读取该管道。Qt 推荐了这样的技巧。

另请阅读信号(7)信号安全(7)(它解释了信号处理程序中可用的有限函数集或系统调用)....

顺便说一句,正确性比效率更重要。通常,您收到的信号很少(例如,大多数程序最多每秒收到一次信号,而不是每毫秒)。

于 2013-09-20T01:20:20.343 回答