【发布时间】:2012-07-25 15:30:05
【问题描述】:
在 Linux 中,当一个程序(可能有多个线程)接收到一个信号(如 SIGTERM 或 SIGHUP)时会发生什么?
哪个线程拦截了信号?多个线程可以得到相同的信号吗?是否有专门用于处理信号的特殊线程?如果不是,处理信号的线程内部会发生什么?信号处理程序完成后如何恢复执行?
【问题讨论】:
标签: c linux multithreading signals ipc
在 Linux 中,当一个程序(可能有多个线程)接收到一个信号(如 SIGTERM 或 SIGHUP)时会发生什么?
哪个线程拦截了信号?多个线程可以得到相同的信号吗?是否有专门用于处理信号的特殊线程?如果不是,处理信号的线程内部会发生什么?信号处理程序完成后如何恢复执行?
【问题讨论】:
标签: c linux multithreading signals ipc
pthreads(7) 描述了 POSIX.1 要求一个进程中的所有线程共享属性,包括:
POSIX.1 还要求每个线程的某些属性不同,包括:
信号掩码 (pthread_sigmask(3))
备用信号堆栈 (sigaltstack(2))
Linux 内核的complete_signal 例程具有以下代码块——cmets 非常有用:
/*
* 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_IGN 或 SIG_DFL,则所有线程都会忽略该信号(或默认值 -- kill、core 或 ignore)。
如果您的进程已将信号的处置设置为特定的处理程序例程,那么您可以通过使用pthread_sigmask(3) 操作特定的线程信号掩码来控制哪个线程将接收信号。您可以指定一个线程来管理所有这些,或者为每个信号创建一个线程,或者这些选项的任意组合用于特定信号,或者您依赖 Linux 内核当前将信号传递到主线程的默认行为。
然而,根据signal(7) 手册页,有些信号是特殊的:
可能会为整个进程生成(并因此挂起)信号 (例如,当使用 kill(2) 发送时)或针对特定线程(例如, 某些信号,例如 SIGSEGV 和 SIGFPE,生成为 执行特定机器语言指令的结果是 线程定向,针对特定线程的信号使用 pthread_kill(3))。一个过程导向的信号可以被传递到任何 当前没有阻塞信号的线程之一。 如果多个线程的信号被解除阻塞,则 内核选择一个任意线程来传递信号。
【讨论】:
这略有细微差别,具体取决于您使用的 Linux 内核版本。
假设 2.6 posix 线程,如果你在谈论操作系统发送 SIGTERM 或 SIGHUP,信号被发送到进程,由根线程接收和处理。使用 POSIX 线程,您也可以将 SIGTERM 发送到各个线程,但我怀疑您是在询问操作系统向进程发送信号时会发生什么。
在 2.6 中,SIGTERM 将导致子线程“干净地”退出,而在 2.4 中,子线程处于不确定状态。
【讨论】: