我一直试图理解POSIX线程和POSIX信号如何交互的复杂性。我特别感兴趣的是:
- 控制信号发送到哪个线程的最佳方法是什么(假设它首先不是致命的)?
- 告诉另一个线程(实际上可能是忙碌的)信号已经到达的最好方法是什么?(我已经知道,使用信号处理程序中的pthread条件变量是个坏主意。)
- 如何安全地处理将信号发生的信息传递给其他线程的问题?这需要在信号处理程序中进行吗?(通常我不想杀死其他线程;我需要一种更微妙的方法。)
关于为什么我需要这个的参考,我正在研究如何转换TclX包来支持线程,或者将其拆分,至少使一些有用的部分支持线程。信号是其中一个特别感兴趣的部分。
4条答案
按热度按时间w8ntj3qf1#
正如@zoli2k所指出的,显式指定一个线程来处理您想要处理的所有信号(或者一组线程,每个线程都具有特定的信号职责)是一种很好的技术。
我不会说“最好”,但我的建议是:
阻塞
main
中所有需要的信号,以便所有线程都继承该信号掩码。然后,将特殊的信号接收线程设计为信号驱动的事件循环,将新到达的信号作为一些其他线程内通信分发。最简单的方法是让线程使用
sigwaitinfo
orsigtimedwait
在循环中接受信号,然后线程以某种方式转换信号,可能是广播pthread_cond_t
,唤醒其他具有更多I/O的线程,在特定于应用程序的线程安全队列中排队命令,等等。或者,该特殊线程可以允许将信号传递到信号处理程序,仅当准备好处理信号时才取消传递的屏蔽。(然而,通过处理程序传递信号往往比通过
sigwait
系列接收信号更容易出错。)在这种情况下,接收器的信号处理程序执行一些简单且异步信号安全的操作:设置sig_atomic_t
标志,调用sigaddset(&signals_i_have_seen_recently, latest_sig)
、write
()一个字节到非阻塞self-pipe,等等。然后,回到其屏蔽的主循环中,如上所述,线程将信号的接收传送到其他线程。(已更新@caf正确地指出
sigwait
方法更上级。)fjnneemd2#
根据POSIX标准,所有线程在系统上都应该以相同的PID出现,使用
pthread_sigmask()
可以为每个线程定义信号阻塞掩码。由于每个PID只允许定义一个信号处理程序,我倾向于在一个线程中处理所有信号,如果需要取消正在运行的线程,则发送
pthread_cancel()
。这是针对pthread_kill()
的首选方法,因为它允许为线程定义清理函数。在一些较旧的系统上,由于缺乏适当的内核支持,运行线程的PID可能与父线程的PID不同。请参阅FAQ了解Linux 2.4上linuxThreads的信号处理。
6yt4nkrj3#
我目前的情况:
我仍然需要整理
signal
与sigaction
、pselect
、sigwait
、sigaltstack
,以及一大堆其他POSIX(和非POSIX)API的细节。fcy6dtqo4#
恕我直言,Unix V信号和posix线程没有很好地混合在一起,Unix V是1970年,POSIX是1980年;)
有取消点,如果你允许信号和pthreads在一个应用程序中,你最终将结束写循环周围的每个调用,这可能会令人惊讶地返回EINTR。
因此,在(少数)必须在Linux或QNX上编写多线程程序的情况下,我所做的是屏蔽所有(除了一个)线程的所有信号。
当一个Unix V信号到达时,进程切换堆栈(在Unix V中,这是你在一个进程中所能得到的最多的并发)。
正如这里的其他帖子所暗示的,现在可能告诉系统哪个posix线程将成为堆栈切换的牺牲品。
一旦你设法让你的信号处理程序线程工作,问题仍然存在,如何将信号信息转换成其他线程可以使用的文明的东西。需要一个线程间通信的基础结构。一个有用的模式是actor模式,其中每个线程都是一些进程内消息传递机制的目标。
因此,您应该尝试将Signal从Signal上下文马歇尔到Signal处理程序线程,然后使用actor模式通信机制将语义上有用的消息发送给那些需要信号相关信息的actor,而不是取消或杀死其他线程(或其他奇怪的事情)。