2011-11-17 70 views
2

我正在研究使用sigprocmask来阻止特定信号(在这种情况下,SIGALRMSIGCHLD),当执行一段关键的代码段时。与这些信号相关联的两个信号处理程序都将访问和修改中央数据结构,所以当主进程正在处理它时,阻止它们访问它是至关重要的。信号执行期间sigprocmask

目前,我的计划是在代码的关键部分开始时简单地禁用这些信号,然后在最后重新启用它们。

void criticalFunction(void) { 
    // disable signals with sigprocmask 
    // critical code 
    // enable signals with sigprocmask 
} 

但是,信号处理程序对于将被阻止的信号也会呼叫criticalFunction。当他们调用sigprocmask函数并启用自己的信号阻塞时会发生什么?他们会失速还是继续执行? (或第三个条件。)

唯一要注意的我能够找到的有关这个如下:

如果sigprocmask()执行被称为一个信号处理程序,从 处理程序返回可以撤销sigprocmask()通过恢复原始的 未决信号掩码的工作。 (http://www.mkssoftware.com/docs/man3/sigprocmask.3.asp

(这是一个后续问题我刚才的问题:Signal handler accessing queue data structure (race condition?)

回答

2

你的设计是错误的,当你说 “信号处理[...]叫criticalFunction。”信号处理程序不应该做任何大量的工作。他们不是为此而制造的。

您应该从信号处理程序中合理地做的唯一事情是修改类型为sigatomic_t的变量。这通常用于设置一个标志,并且代码的其余部分(例如主循环)只需定期检查是否设置了任何标志。

我认为它实际上是未定义的行为如果一个信号处理程序做了除此之外的任何事情。 更新:man 2 signal:“请参阅signal(7)了解可从信号处理程序内安全调用的异步信号安全函数列表。”

+0

POSIX定义了在信号处理程序中执行某些操作的行为,并且您可以执行相当多的操作。这只是非POSIX,普通的C环境,你无法在信号处理程序中做任何事情,并且在这样的环境中,无论如何都没有有用的信号,对于信号处理程序来说没有用处...... –

+0

@ R:是的确,所有的“异步信号安全”功能都可以。特别是,您可以更改信号处理程序内的信号掩码和其他与信号有关的东西。但就一般用户代码而言,最好不要使用信号处理程序。否则,你必须证明你所做的每一件事情都是异步信号安全的,而且这可能是非本地的,很难做到。 –

+0

如果您知道异步信号不安全的功能未被信号处理程序中断,您可以自由地从信号处理程序调用**任何**功能。如果您仔细地屏蔽并揭露信号,即使在多线程代码中,也很容易确保这一点。 –

4

请记住,信号处理程序内部的默认行为是阻止正在处理的信号。在信号处理程序内部进行函数调用时,只需调用信号安全函数。这就是说,sigprocmask()是一个signal-safe function,如果你使用它来阻止信号处理程序阻塞的信号被内部调用,那么真的什么都不会发生......你将继续与您目前拥有的信号掩码相同。唯一的区别是,在信号处理器内部,只有SIGALRMSIGCHLD的信号被保证被阻塞(这取决于您所在的信号处理程序),其中 - 当您拨打sigprocmask()来阻止这些特定信号时,这两个信号将在通话后被阻止。

需要注意的事情是在criticalFunction代码的第二部分,当你试图调用sigprocmask()使当前挡在信号屏蔽的信号。这可能会造成一种情况,最终会导致对信号处理程序的调用重新进入。换句话说,为信号处理程序启用信号可能意味着在退出当前信号处理程序之前,会捕获另一个SIGALRMSIGCHLD,并且您将再次重新输入信号处理程序以处理新的问题抓到信号。只要你在任何关键部分更新后启用信号,那么我认为你应该适应这种重入状况,但只是为了安全起见,你可能只想启用criticalFunction中的信号criticalFunction的末尾,而不是在中间的某个位置,并且当您从criticalFunction返回时,请不要做任何不会异步安全的操作......您必须假定第二个sigprocmask()返回后的任何代码可能不是按顺序执行(即它可能在第二个信号被捕获并且其信号处理程序运行后执行)。

如果您试图从exec家族中调用某些东西,或者您的信号处理程序中存在某种性质的东西,那么您只需要关心“失速”。会发生什么情况是新覆盖的进程会继承当前进程的信号掩码,因此如果当前进程阻塞了某些信号,那么它们也将在新进程中被阻塞。因此,如果新进程假定信号被解除阻塞,那么新进程中的信号处理程序将永远不会运行。

顺便说一句,一个警告:不要混合信号和线程!你在你的问题中提到“主要过程”......我希望这并不意味着你正试图混合信号和线程。如果是这样,那就需要一个非常具体的习惯用语,否则你会造成各种破坏。

+2

通过使用'sigprocmask()'恢复'criticalFunction()'末尾的前一个信号掩码,而不是盲目解除你关心的信号(即在'criticalFunction ()'''''''''''''''''''''''''sigprocmask(SIG_BLOCK,&critical_sigs,&old_sigs);'最后你会做'sigprocmask(SIG_SETMASK,&old_sigs,NULL);'。 – caf

+0

我没有在这个系统中使用线程 - 信号是为了避免使用线程对不起,选择不好的字 – BSchlinker

+0

@Jason,从信号处理程序修改无锁数据结构是否安全?sigprocmask()被认为是解决我的问题的可能方法在我以前的(链接)的问题。但是,从你的链接看起来像修改一个队列仍然可能导致腐败。我想我可以使用一个无锁队列,而不是确定是否放置所有问题。 – BSchlinker