Linux中具有多个线程的信号处理


119

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

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

Answers:


35

根据您所使用的Linux内核版本,这会有细微差别。

假设有2.6个posix线程,并且如果您正在谈论操作系统正在发送SIGTERM或SIGHUP,则信号将发送到进程,该进程由根线程接收并处理。使用POSIX线程,您也可以将SIGTERM发送到各个线程,但是我怀疑您是在询问操​​作系统将信号发送到进程时会发生什么。

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


当收到信号时,根线程内部会发生什么?假设我为SIGUSR1编写了一个自定义信号处理程序,现在我将该信号发送给进程。根线程将获得该信号。也许那时它正在某个功能的中间。会发生什么?

1
如果您有处理程序设置,它将被视为中断,并且程序流将停止并且您的自定义处理程序将被执行。一旦执行完,假定您没有做任何改变正常流程(退出等)的操作,控制权将返回。
艾伦(Alan)

请注意,这特定于SIGUSR1,IIRC不会中断系统调用。例如,如果您尝试使用SIGINT进行操作,它可能会中断流读取,而当您返回读取时,该流可能会返回一个错误,表明该流已被中断。
艾伦(Alan)

10
我对“根线程”的含义有些困惑。这是否意味着SIGTERM的处理程序将始终在主线程中运行,或者可以在任何线程中运行?
Stephen Nutt 2014年

3
该回答表明您选择了任意线程来处理信号,这与您的回答相矛盾。
user202729

134

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或ignore)。

如果您的进程已将信号的处理方式设置为特定的处理程序例程,则可以通过使用操纵特定的线程信号掩码来控制哪个线程将接收信号pthread_sigmask(3)。您可以指定一个线程来管理所有线程,或者为每个信号创建一个线程,或者为特定信号混合使用这些选项,或者您依靠Linux内核当前将信号传递到主线程的默认行为。

但是,根据signal(7)手册页,某些信号是特殊的:

可以为整个过程(例如,当使用kill(2)发送时)或特定线程(例如,由于执行而生成的某些信号,例如SIGSEGV和SIGFPE )生成(并因此挂起)信号特定的机器语言指令是线程定向的,使用pthread_kill(3)锁定特定线程的信号也是如此 。可以将过程控制的信号传递到当前未阻塞信号的任何一个线程。如果多个线程中的信号畅通无阻,则内核选择一个任意线程来将信号传递到该线程。

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.