Обработка сигналов с несколькими потоками в Linux


в Linux, что происходит, когда программа (которая, возможно, имеет несколько потоков) получает сигнал, как SIGTERM или SIGHUP?

какой поток перехватывает сигнал? Может ли несколько потоков получить один и тот же сигнал? Есть ли специальный поток, полностью посвященный обработке сигналов? Если нет, то, что происходит внутри потока, который обрабатывает сигнал? Как возобновляется выполнение после завершения процедуры обработчика сигналов?

2 96

2 ответа:

Это немного нюансы, основанные на том, какую версию ядра Linux вы используете.

предполагая, что потоки 2.6 posix, и если вы говорите об отправке ОС SIGTERM или SIGHUP, сигнал отправляется процессу, который принимается и обрабатывается корневым потоком. Используя потоки POSIX, вы также можете отправить SIGTERM отдельным потокам, но я подозреваю, что вы спрашиваете о том, что происходит, когда ОС отправляет сигнал процессу.

в 2.6, SIGTERM вызовет дочерние потоки для выхода "чисто", где, как 2.4, дочерние потоки были оставлены в неопределенном состоянии.

pthreads(7) описывает этот POSIX.1 требуется, чтобы все потоки в процессе имели общие атрибуты, в том числе:

  • диспозиции сигнала

POSIX.1 также требует, чтобы некоторые атрибуты были distinct для каждого потока, в том числе:

  • маска сигнала (pthread_sigmask(3))

  • альтернативный стек сигналов (sigaltstack(2))

ядро 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_UNKILLABLE | SIGNAL_GROUP_EXIT)) &&
        !sigismember(&t->real_blocked, sig) &&
        (sig == SIGKILL || !t->ptrace)) {
            /*
             * This signal will be fatal to the whole group.
             */

Итак, вы видите, что вы отвечают за то, где сигналы доставляются:

если ваш процесс установил расположение сигнала в SIG_IGN или SIG_DFL, затем сигнал игнорируется (или по умолчанию -- kill, core или ignore) для всех потоков.

если ваш процесс установил расположение сигнала к определенной подпрограмме обработчика, то вы можете контролировать, какой поток будет получать сигналы манипулирование определенными масками сигналов потока с помощью pthread_sigmask(3). Вы можете назначить один поток для управления ими всеми, или создать один поток для каждого сигнала, или любую смесь этих параметров для конкретных сигналов, или вы полагаетесь на текущее поведение ядра Linux по умолчанию доставки сигнала в основной поток.

некоторые сигналы, однако особенные:

   A signal may be generated (and thus pending) for a process as
   a whole (e.g., when sent using kill(2)) or for a specific
   thread (e.g., certain signals, such as SIGSEGV and SIGFPE,
   generated as a consequence of executing a specific machine-
   language instruction are thread directed, as are signals
   targeted at a specific thread using pthread_kill(3)).  A
   process-directed signal may be delivered to any one of the
   threads that does not currently have the signal blocked.  If
   more than one of the threads has the signal unblocked, then
   the kernel chooses an arbitrary thread to which to deliver
   the signal.