我正在开发一个涉及信号驱动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只发现队列为空并完成,或者我是否会因某种错误而面临风险?
答案 0 :(得分:1)
如果您正在使用sigaction
来设置信号处理程序,那么导致处理程序被触发的信号将默认已在处理程序内被阻止。
在你的代码中,然后阻止所有信号,然后恢复旧掩码是关于阻止所有其他信号,原始信号(触发处理程序)将被阻止,直到你从处理程序(或者你专门解锁它)。
使用sigaction
,您可以通过设置sa_mask
的{{1}}字段来避免执行此阻止并在处理程序中恢复,struct sigaction
字段是处理程序中阻止的信号集。
此外,您使用signal
来重新设置"处理程序有点令人困惑,你调用函数handle_signal
然后重新设置为catch_int
(假设这个处理程序实际上正在处理SIGINT
...
旧的signal
API确实用于在每次触发处理程序时将信号处理程序重置为默认值。但是,默认情况下sigaction
不会执行此操作,因此您不需要重新设置"如果您正在使用sigaction
API,请使用信号处理程序。我个人会避免在同一个程序中混合调用signal
和sigaction
,我会选择一个并坚持下去。
总之,我认为您对sigprocmask
不是原子的担忧是不必要的,因为有问题的信号已被阻止,您signal
和sigaction
的混合使用让我更担心。< / p>