信号会传递给在 POSIX 信号量上阻塞的程序吗?

Will signals be delivered to a program blocked on POSIX semaphore?

这确实是两个问题,但我想把它们结合起来会更好。

我们正在开发使用异步 TCP 连接的客户端。这个想法是程序将阻塞,直到从服务器接收到特定消息,这将调用 SIGPOLL 处理程序。我们正在使用繁忙的等待循环,基本上:

var = 1
while (var) usleep(100);

//...and somewhere else
void sigpoll_handler(int signum){
    ......
    var = 0;
    ......
}

我们想使用更可靠的东西,比如信号量。问题是,当一个线程被信号量阻塞时,信号还能通过吗?特别是考虑到信号在切换回用户级别时得到传递;如果进程不在运行队列中,将如何发生?

附带问题(出于好​​奇):

如果没有 "usleep(100)",程序将永远不会通过 while 循环,尽管我可以验证变量是否已在处理程序中设置。这是为什么?打印也会改变它的行为。

干杯!

简短回答:是的,如果实施得当,信号会顺利通过。

如果您打算使用信号量来控制程序的流程,您将希望在一个 child 上进行侦听,在另一个上进行实际数据处理。然后,这会将并发公平性置于 OS 手中,这将确保您的信号侦听线程有机会以某种规律性检查信号。它不应该是真正的 "off the runqueue,",而是循环遍历 运行 队列中的位置。

如果它能帮助您考虑的话,您现在所拥有的似乎基本上是信号量本身的一个非常粗略的实现 -- 一个共享变量,其值将阻止一个代码块执行直到另一个代码块清除它。在系统级别上,信号量没有任何内在的瘫痪。

我有点想知道为什么您用来侦听 SIGPOLL 的任何函数都没有进行自己的阻塞。我见过的大多数实用程序都会停止它们的调用线程,直到它们 return 一个值。基本上他们为您处理并发,您可以像处理普通同步程序一样编写代码。

关于 usleep 循环:我必须看看优化器在做什么,但我认为基本上有两种可能性。我认为这不太可能,但可能是 no-body 循环正在编译成实际上不检查值更改而只是循环的东西。对我来说更有可能的是,缺少任何正文步骤会扰乱底层的并发处理,并且循环执行得如此之快以至于没有其他任何东西有机会 运行 -- 队列正在被循环淹没迭代和你的信号处理不能在边缘得到一个词。您可以尝试只看几个小时,看看是否有任何变化;从理论上讲,如果它只是一个并发问题,那么所涉及的随机因素可以以几十亿次的机会自行清除块。

[评论太长]

从信号处理程序内部访问 var 会调用未定义的行为(至少对于符合 POSIX 的系统而言)。

来自the related POSIX specification

[...] if the process is single-threaded and a signal handler is executed [...] the behavior is undefined if the signal handler refers to any object [...] with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t [...]

所以var应定义为:

volatile sig_atomic_t var;

忙等待 while 循环,可以由对阻塞 pause() 的单个调用代替,因为它会 return 接收到信号。

来自the related POSIX specification

The pause() function shall suspend the calling thread until delivery of a signal whose action is either to execute a signal-catching function or to terminate the process.

顺便说一句,使用 pause() 将使任何全局标志(如 var 的使用变得多余,更不用说不必要了。