我正在研究使用sigprocmask
来阻止某些关键代码段执行时的某些信号(在本例中为SIGALRM
和SIGCHLD
)。与这些信号相关联的两个信号处理程序都将访问和修改中央数据结构,因此在主进程处理它时阻止它们访问它是至关重要的。
目前,我的计划是在代码的关键部分开始时简单地禁用这些信号,然后在最后重新启用它们。
void criticalFunction(void) {
// disable signals with sigprocmask
// critical code
// enable signals with sigprocmask
}
但是,要阻止的信号的信号处理程序也会调用criticalFunction
。当他们调用sigprocmask
函数并对自己的信号启用阻塞时会发生什么?它们会停止还是继续执行? (或者某些第三种情况......)
我能找到的关于此的唯一注意事项如下:
如果在信号处理程序中调用sigprocmask(),则返回 handler可以通过恢复原始函数撤消sigprocmask()的工作 待定信号掩码。 (http://www.mkssoftware.com/docs/man3/sigprocmask.3.asp)
(这是我上一个问题的后续问题:Signal handler accessing queue data structure (race condition?))
答案 0 :(得分:4)
请记住,信号处理程序内部的默认行为是阻止正在处理的信号。此外,当在信号处理程序内部进行函数调用时,您只想调用信号安全函数。话虽如此,sigprocmask()
是一个signal-safe function,如果你用它来阻止被信号处理程序阻塞的相同信号,那么它就被调用了,那么真的什么都不会发生......你将继续使用你目前拥有的相同信号掩码。唯一的区别是在信号处理程序内部,只保证SIGALRM
或SIGCHLD
的信号被阻止(它将取决于你所处的信号处理程序),其中 - 当你拨打sigprocmask()
来阻止这些特定的信号,这两个信号在通话后都会被阻止。
当您尝试将criticalFunction
调用启用信号中当前被阻止的信号时,需要注意的是代码的第二部分sigprocmask()
面具。这可能会导致您在信号处理程序调用中达到一定程度的重新进入的情况。换句话说,启用信号处理程序的信号可能意味着在退出当前信号处理程序之前,另一个SIGALRM
或SIGCHLD
被捕获,您将重新输入信号处理程序再次处理这个新捕获的信号。只要您在任何关键部分更新后启用信号,那么我认为您应该对这种重入情况很好,但为了安全起见,您可能只想在{{1}中启用信号在criticalFunction
的最后,而不是在中间的某个地方,当你从criticalFunction
返回时,不要做任何不是异步安全的事情......你必须假设返回第二个criticalFunction
之后的任何代码可能没有按顺序执行(即,它可能在捕获第二个信号并且其信号处理程序运行后执行)。
如果您试图从sigprocmask()
系列中调用某些内容,或者信号处理程序内部的某些内容,您只需要关注“拖延”。会发生什么情况是新覆盖的进程将从当前进程继承信号掩码,因此如果当前进程阻塞了某些信号,那么它们也将在新进程中被阻塞。因此,如果新进程假设信号未被阻塞,则新进程中的信号处理程序将永远不会运行。
答案 1 :(得分:2)
当您说“信号处理程序[...]呼叫criticalFunction
时,您的设计是错误的。”信号处理人员不应该做任何大量的工作。他们不是为此而做的。
您应该在信号处理程序中合理地做的唯一事情是修改sigatomic_t
类型的变量。这通常用于设置标志,代码的其余部分(例如主循环)只需定期检查是否已设置任何标志。
如果信号处理程序执行除此之外的任何操作,我认为它实际上是未定义的行为。 更新:来自man 2 signal
:“请参阅signal(7)
以获取可从信号处理程序内安全调用的异步信号安全函数列表。”