如何避免信号处理程序中的竞争条件

时间:2017-07-17 21:24:48

标签: c concurrency signals race-condition

我正在开发一个涉及信号驱动I / O的项目,最终可能会使用sigaction()创建的信号处理程序。我担心的是处理程序可能被多次调用。换句话说,它将处理消息A,当它被消息B中断并开始处理B时,可能导致问题。我在网上看到一些使用sigprocmask来避免这种情况的代码,但对我来说似乎不对。例如:

void handle_signal(int sig_num)
{
    sigset_t mask_set;  /* used to set a signal masking set. */
    sigset_t old_set;   /* used to store the old mask set.   */

    /* re-set the signal handler again to catch_int, for next time */
    signal(SIGINT, catch_int);
    /* mask any further signals while we're inside the handler. */
    sigfillset(&mask_set);
    sigprocmask(SIG_SETMASK, &mask_set, &old_set);

    .... (content handling code here) ....

    /* restore the old signal mask */{{/COMMENT_FONT}*/
    sigprocmask(SIG_SETMASK, &old_set, NULL);
}

这显然是错误的,因为sigprocmask与信号不是原子的。换句话说,在调用信号处理程序和调用sigprocmask之间有一个时间窗口,并且可以在该窗口中第二次或第三次调用信号处理程序,从而创建竞争条件。

我的选择:

(1)使用处理程序内部的信号量来排队处理程序的任何冗余调用。

(2)将处理程序写为可重入的,因此可以同时多次调用它。

(3)其他一些解决方案?

如果我选择上面的选项(2),我可以认为套接字读取队列是线程安全的吗?例如,让我们说套接字处理程序被调用两次。实例A开始从套接字读取,然后它被中断,实例B开始完成读取数据包。这是否会导致A只发现队列为空并完成,或者我是否会因某种错误而面临风险?

1 个答案:

答案 0 :(得分:1)

如果您正在使用sigaction来设置信号处理程序,那么导致处理程序被触发的信号将默认已在处理程序内被阻止。

在你的代码中,然后阻止所有信号,然后恢复旧掩码是关于阻止所有其他信号,原始信号(触发处理程序)将被阻止,直到你从处理程序(或者你专门解锁它)。

使用sigaction,您可以通过设置sa_mask的{​​{1}}字段来避免执行此阻止并在处理程序中恢复,struct sigaction字段是处理程序中阻止的信号集。

此外,您使用signal来重新设置"处理程序有点令人困惑,你调用函数handle_signal然后重新设置为catch_int(假设这个处理程序实际上正在处理SIGINT ...

旧的signal API确实用于在每次触发处理程序时将信号处理程序重置为默认值。但是,默认情况下sigaction不会执行此操作,因此您不需要重新设置"如果您正在使用sigaction API,请使用信号处理程序。我个人会避免在同一个程序中混合调用signalsigaction,我会选择一个并坚持下去。

总之,我认为您对sigprocmask不是原子的担忧是不必要的,因为有问题的信号已被阻止,您signalsigaction的混合使用让我更担心。< / p>