Linux 中的多线程信号处理

在 Linux 中,当一个程序(可能有多个线程)接收到一个信号(如 SIGTERM 或 SIGHUP)时会发生什么?

哪个线程拦截信号?多个线程能得到相同的信号吗?是否有专门用于处理信号的特殊线程?如果没有,线程内部会发生什么事情来处理信号?信号处理程序例程完成后,如何恢复执行?

82288 次浏览

基于您正在使用的 Linux 内核的版本,这一点有些微妙。

假设有2.6个 posx 线程,如果您正在讨论发送 SIGTERM 或 SIGHUP 的 OS,信号将被发送到 process,由 root 线程接收和处理。使用 POSIX 线程,您还可以将 SIGTERM 发送到单个线程,但我怀疑您是在询问当操作系统向进程发送信号时会发生什么。

在2.6中,SIGTERM 将导致子线程“干净地”退出,而在2.4中,子线程处于不确定状态。

pthreads(7) 描述 POSIX.1需要进程共享属性中的所有线程,包括:

  • 信号配置信号配置信号配置

POSIX.1还要求每个线程的某些属性为 很明显,包括:

Linux 内核的 complete_signal例程有以下代码块——注释非常有用:

/*
* Now find a thread we can wake up to take the signal off the queue.
*
* If the main thread wants the signal, it gets first crack.
* Probably the least surprising to the average bear.
*/
if (wants_signal(sig, p))
t = p;
else if (!group || thread_group_empty(p))
/*
* There is just one thread and it does not need to be woken.
* It will dequeue unblocked signals before it runs again.
*/
return;
else {
/*
* Otherwise try to find a suitable thread.
*/
t = signal->curr_target;
while (!wants_signal(sig, t)) {
t = next_thread(t);
if (t == signal->curr_target)
/*
* No thread needs to be woken.
* Any eligible threads will see
* the signal in the queue soon.
*/
return;
}
signal->curr_target = t;
}


/*
* Found a killable thread.  If the signal will be fatal,
* then start taking the whole group down immediately.
*/
if (sig_fatal(p, sig) &&
!(signal->flags & SIGNAL_GROUP_EXIT) &&
!sigismember(&t->real_blocked, sig) &&
(sig == SIGKILL || !p->ptrace)) {
/*
* This signal will be fatal to the whole group.
*/

所以,你可以看到 负责信号的传递:

如果您的进程已经将信号的配置设置为 SIG_IGNSIG_DFL,那么对于所有线程,信号将被忽略(或默认值为 kill、 core 或忽略)。

如果您的进程已经将信号的配置设置为特定的处理程序例程,那么您可以通过使用 pthread_sigmask(3)操作特定的线程信号掩码来控制哪个线程将接收信号。您可以指定一个线程来管理它们,或者为每个信号创建一个线程,或者为特定信号创建这些选项的任意混合,或者您可以依赖于 Linux 内核当前的默认行为,即将信号传递给主线程。

然而,根据 signal(7)手册页,有些信号是特殊的:

作为一个整体,可以为一个进程生成一个信号(并因此等待) (例如,当使用 杀死(2)发送时)或对于特定的线程(例如, 某些信号,例如 SIGSEGV 和 SIGFPE,作为 执行特定机器语言指令的结果是 针对特定线程的信号使用 Pthread _ kill (3) 当前没有阻塞信号的线程之一。 如果多个线程的信号未阻塞,则 内核选择一个任意的线程来传递信号。