POSIX线程和信号


81

我一直在尝试理解POSIX线程和POSIX信号如何交互的复杂性。我特别感兴趣:

  • 控制信号传递到哪个线程的最佳方法是什么(假设它首先不会致命)?
  • 告诉另一个线程(实际上可能很忙)信号到达的最佳方法是什么?(我已经知道,使用信号处理程序中的pthread条件变量是一个坏主意。)
  • 如何安全处理将发生信号的信息传递给其他线程?这是否需要在信号处理程序中发生?(我通常不希望杀死其他线程;我需要一种更巧妙的方法。)

作为我为什么要这样做的参考,我正在研究如何将TclX包转换为支持线程,或者将其拆分并至少使一些有用的部件支持线程。信号是那些特别令人感兴趣的部分之一。

Answers:


48
  • 控制信号传递到哪个线程的最佳方法是什么?

正如@ zoli2k所指出的那样,显式地指定一个线程来处理您要处理的所有信号(或一组线程,每个线程都具有特定的信号职责)是一种很好的技术。

  • 告诉另一个线程(实际上可能很忙)信号到达的最佳方法是什么?[...]
  • 如何安全处理将发生信号的信息传递给其他线程?这是否需要在信号处理程序中发生?

我不会说“最好”,但这是我的建议:

阻止中的所有所需信号main,以便所有线程都继承该信号掩码。然后,将特殊信号接收线程设置为信号驱动事件循环,将新到达的信号作为其他线程内通信进行调度。

最简单的方法是让线程使用sigwaitinfosigtimedwait在循环中接受信号。然后,线程以某种方式转换信号,也许是广播a pthread_cond_t,唤醒具有更多I / O的其他线程,将命令排队在特定于应用程序的线程安全队列中,无论如何。

或者,特殊线程可以允许将信号传递到信号处理程序,仅在准备处理信号时才取消传递屏蔽。(经处理的信号传递往往比通过信号接收更容易出错sigwait的家庭,但是。)在这种情况下,接收器的信号处理程序执行一些简单的和异步信号安全操作:设置sig_atomic_t标志,调用sigaddset(&signals_i_have_seen_recently, latest_sig)write()字节到非阻塞自管等。然后,回到它掩盖主循环中,线程通信接收到信号给其他线程如上的。

UPDATED @caf正确地指出sigwait方法是更好的。)


1
这是一个非常有用的答案,尤其是因为它也可以用于处理非致命信号处理时。谢谢!
Donal Fellows 2010年

1
如果信号处理线程根本不安装信号处理程序,这是最简单的-相反,它在sigwaitinfo()(或sigtimedwait())上循环,然后如上一段所述将它们分派到应用程序的其余部分。
caf 2015年

@caf,的确如此。更新
pilcrow

14

根据POSIX标准,所有线程在系统上应以相同的PID出现,并且使用pthread_sigmask()可以为每个线程定义信号阻止掩码。

由于只允许为每个PID定义一个信号处理程序,因此我更喜欢在一个线程中处理所有信号,并pthread_cancel()在需要取消正在运行的线程时发送。这是首选的方法,pthread_kill()因为它允许为线程定​​义清理功能。

在某些较旧的系统上,由于缺少适当的内核支持,正在运行的线程可能具有与父线程的PID不同的PID。有关在Linux 2.4上使用linuxThreads处理信号的信息,请参阅FAQ 。


您所说的“实施”是什么意思?同样,总是不停地对其他线程进行响应以响应信号(SIGHUP和SIGWINCH需要更多的微妙之处)也是不正确的,但是使用条件变量来告知其他线程并不安全。可怜的答案。
Donal Fellows 2010年

1
删除了我的不赞成票,但这仍然不是一个足够的答案,因为我不能仅仅根据响应信号杀死线程。在某些情况下,我将在本地对事件进行排队以作为响应,而在另一些情况下,我必须非常小心地拆除线程(顺便说一句,我已经拥有大多数机器来做那些部分;这是与操作系统的连接)缺少的信号)。
Donal Fellows 2010年

1
@ zoli2k:我最近尝试make menuconfig使用uClibc的新克隆的git master分支运行。还有因为2012年还是建议不要选择NPTL的原有的LinuxThreads和NPTL新的POSIX线程的实现,但帮助之间的选择。因此,即使系统正在运行足够的最新Linux内核,在现代嵌入式Linux系统中仍然普遍看到过时的LinuxThreads实现。
FooF

3

到目前为止,我在哪里:

  • 信号来自不同的主要类别,其中一些通常通常应该终止进程(SIGILL),而某些则永远不需要做任何事情(SIGIO;更容易直接进行异步IO)。这两个类无需采取任何措施。
  • 有些信号不需要立即处理。SIGWINCH之类的内容可以排队直到方便为止(就像X11中的事件一样)。
  • 棘手的是那些您想通过中断正在执行的操作来对它们做出响应,而又不会消灭线程的程度。特别是,在交互模式下的SIGINT应该使事物响应。

我还有需要整理signalVS sigactionpselectsigwaitsigaltstack,和一大堆其他位和POSIX(和非POSIX)API的作品。


3

恕我直言,Unix V信号和posix线程混合得不好。Unix V是1970。POSIX是1980;)

有取消点,如果在一个应用程序中允许信号和pthread,则最终将最终在每个调用周围编写循环,这可能令人惊讶地返回EINTR。

因此,在(很少)必须在Linux或QNX上编写多线程程序的情况下,我所做的就是屏蔽所有(仅一个)线程的所有信号。

当Unix V信号到达时,进程切换堆栈(在Unix V中,并发程度与进程中获得的并发程度相同)。

正如此处的其他帖子所暗示的那样,现在有可能告诉系统哪个posix线程将成为该堆栈切换的受害者。

一旦您设法使Signal handler线程正常工作,问题仍然存在,如何将信号信息转换为文明的东西,其他线程可以使用。需要线程间通信的基础结构。一种有用的模式是参与者模式,其中每个线程都是某些进程内消息传递机制的目标。

因此,您应该尝试将信号从Signal上下文编组到Signal处理程序线程中,而不是取消其他线程或杀死它们(或其他怪异的东西),然后使用actor模式通信机制将语义上有用的消息发送给这些actor,谁需要信号相关的信息。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.