如果 Unix (Posix) 进程接收到信号,则会运行信号处理程序。
在多线程进程中会发生什么?哪个线程接收信号?
我认为,信号 API 应该扩展以处理该问题(即应该能够确定信号处理程序的线程),但在网上搜索信息时,我只在 Linux 内核邮件列表和不同论坛上找到了一年多的争论。据我了解,Linus 的概念与 Posix 标准不同,首先构建了一些兼容层,但现在 Linux 遵循 posix 模型。
目前的状况如何?
答案1
POSIX 中的条目“信号生成和传输”在“基本原理:系统接口一般信息”中说
为进程生成的信号仅传递给一个线程。因此,如果多个线程有资格接收信号,则必须选择一个线程。线程的选择完全取决于实现,既允许尽可能广泛的一致实现,又让实现可以自由地将信号传递到“最简单的”线程(如果不同线程之间的传递难易程度存在差异)。
来自signal(7)
Linux 系统上的手册:
可以为整个进程生成(并因此挂起)信号(例如,当使用
kill(2)
)或对于特定线程(例如,由于执行特定机器语言指令而生成的某些信号,例如 SIGSEGV 和 SIGFPE 是线程定向的,就像针对特定线程的信号一样pthread_kill(3)
)。进程控制的信号可以被传递到当前没有阻止该信号的任何一个线程。如果多个线程的信号未被阻塞,则内核会选择任意一个线程来向其传递信号。
并且在pthreads(7)
:
线程具有不同的备用信号堆栈设置。但是,新线程的备用信号堆栈设置是从创建它的线程复制的,以便线程最初共享备用信号堆栈(在内核 2.6.16 中修复)。
来自pthreads(3)
OpenBSD 系统上的手册(作为替代方法的示例):
信号处理程序通常在当前执行线程的堆栈上运行。
(我目前不知道当多个线程在多处理器机器上并发执行时如何处理)
POSIX 线程的较旧 LinuxThread 实现仅允许不同的单个线程成为信号的目标。从pthreads(7)
在 Linux 系统上:
LinuxThreads 不支持进程定向信号的概念:信号只能发送到特定线程。
答案2
扩展已接受的答案,有一个更实际的观点,我发现了什么这里。
本质如下:
信号处理程序是每个进程的,但信号掩码是每个线程的。
- 因此,如果我们安装/卸载信号处理程序(使用信号()或者sigaction())在任何线程上,它都会影响所有线程。
- 如果进程收到信号,则处理程序将仅在单个线程上执行。该线程是在其中伪随机选择的,其信号掩码接受它。我的实验表明,它总是具有最少pid的线程。*
- 发送到任何线程的信号都被视为发送到主进程的信号。因此,如果一个线程收到信号,其他线程很可能会执行处理程序。如果我们看到线程(由
tid
s、线程 id 标识)将被视为屏蔽进程(由pid
s 标识),并且发送到 a 的信号tid
将转发到它们的pid
. - 对于信号处理程序的执行,在其信号掩码中,给定的信号号被自动掩码。这是为了防止在信号突发中执行堆叠信号处理程序。这可以通过调用
SA_NODEFER
的标志来更改sigaction(...)
。 - (3) 和 (4) 的结果是,在信号突发的情况下,系统可能最并行地分布信号处理程序。
- 但是,如果我们设置了 sigaction
SA_NODEFER
,总是同一个线程会收到信号并且它们会堆叠。
*评论说它也可能是首先创建的线程。两者都符合 posix 标准,因此不要相信您的代码中的它。