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

119

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

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


источник

Ответы:

35

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

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

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

Алан
источник
А что происходит внутри корневого потока при получении сигнала? Допустим, я написал собственный обработчик сигналов для SIGUSR1, и теперь я отправляю этот сигнал процессу. Этот сигнал получит корневой поток. Может быть, в этот момент он выполняет какую-то функцию. Что сейчас произойдет?
1
если у вас настроен обработчик, это будет рассматриваться как прерывание, выполнение программы остановится, а ваш собственный обработчик будет выполнен. После его выполнения управление вернется, если вы ничего не сделали, чтобы изменить нормальный поток (выход и т. Д.).
Алан
Обратите внимание, что это характерно для SIGUSR1, который IIRC не прерывает системные вызовы. Если вы попробуете это, например, с помощью SIGINT, это может прервать чтение потока, а когда вы вернетесь к чтению, поток может вернуть ошибку, что он был прерван.
Алан
10
Я немного запутался в том, что подразумевается под "корневой нитью". Означает ли это, что обработчик SIGTERM всегда будет работать в основном потоке, или он может работать в любом потоке?
Стивен Натт
3
Этот ответ , в котором говорится, что для обработки сигнала выбран произвольный поток, противоречит вашему ответу.
user202729
135

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

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

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

complete_signalПодпрограмма ядра Linux имеет следующий блок кода - комментарии весьма полезны:

/*
 * 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) ). Сигнал, управляемый процессом, может быть доставлен в любой из потоков, который в настоящее время не заблокировал сигнал. Если сигнал разблокирован более чем в одном потоке, то ядро ​​выбирает произвольный поток, которому следует доставить сигнал.

sarnold
источник