Tratamento de sinais com vários threads no Linux

119

No Linux, o que acontece quando um programa (que possivelmente possui vários threads) recebe um sinal, como SIGTERM ou SIGHUP?

Qual thread intercepta o sinal? Vários threads podem obter o mesmo sinal? Existe um segmento especial dedicado inteiramente ao tratamento de sinais? Se não, o que acontece dentro do thread que vai tratar o sinal? Como a execução continua após a conclusão da rotina do manipulador de sinais?


fonte

Respostas:

35

Isso tem algumas nuances, com base na versão do kernel do Linux que você está usando.

Assumindo threads 2.6 posix, e se você estiver falando sobre o SO enviando SIGTERM ou SIGHUP, o sinal é enviado para o processo, que é recebido e tratado pelo thread raiz. Usando threads POSIX, você também pode enviar SIGTERM para threads individuais, mas suspeito que você esteja perguntando sobre o que acontece quando o sistema operacional envia o sinal para o processo.

No 2.6, o SIGTERM fará com que os threads filhos saiam "corretamente", enquanto no 2.4, os threads filhos foram deixados em um estado indeterminado.

Alan
fonte
E o que acontece dentro do thread raiz quando um sinal é recebido? Digamos que eu escrevi um manipulador de sinal personalizado para SIGUSR1 e agora estou enviando esse sinal para o processo. O thread raiz receberá esse sinal. Talvez esteja no meio de alguma função naquele momento. O que vai acontecer?
1
se você tiver um manipulador configurado, ele será tratado como uma interrupção, o fluxo do programa será interrompido e seu manipulador personalizado será executado. Uma vez executado, o controle retornará, assumindo que você não fez nada para alterar o fluxo normal (saída etc).
Alan
Observe que isso é específico para SIGUSR1, que IIRC não interrompe chamadas do sistema. Se você tentar fazer isso com o SIGINT, por exemplo, ele pode interromper a leitura de um fluxo e, quando você voltar a ler, o fluxo pode retornar um erro informando que foi interrompido.
Alan
10
Estou um pouco confuso sobre o que significa "thread raiz". Isso significa que o manipulador para SIGTERM sempre será executado no thread principal ou pode ser executado em qualquer thread?
Stephen Nutt,
3
Esta resposta , que afirma que um thread arbitrário é escolhido para lidar com o sinal, contradiz sua resposta.
user202729
134

pthreads(7) descreve que POSIX.1 requer que todos os threads em um processo compartilhem atributos, incluindo:

  • disposições de sinal

POSIX.1 também requer que alguns atributos sejam distintos para cada thread, incluindo:

A complete_signalrotina do kernel Linux possui o seguinte bloco de código - os comentários são bastante úteis:

/*
 * 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.
         */

Então, você vê que você está no comando de onde os sinais são entregues:

Se o seu processo definiu a disposição de um sinal para SIG_IGNou SIG_DFL, então o sinal é ignorado (ou padrão - kill, core ou ignore) para todos os threads.

Se o seu processo definiu a disposição de um sinal para uma rotina de tratamento específica, então você pode controlar qual thread receberá os sinais, manipulando máscaras de sinal de thread específicas usando pthread_sigmask(3) . Você pode nomear um thread para gerenciar todos eles, ou criar um thread por sinal, ou qualquer mistura dessas opções para sinais específicos, ou você pode contar com o comportamento padrão atual do kernel Linux de entregar o sinal ao thread principal.

Alguns sinais, no entanto, são especiais de acordo com a signal(7)página do manual:

Um sinal pode ser gerado (e, portanto, pendente) para um processo como um todo (por exemplo, quando enviado usando kill (2) ) ou para uma thread específica (por exemplo, certos sinais, como SIGSEGV e SIGFPE, gerados como consequência da execução uma instrução específica em linguagem de máquina é direcionada por thread, assim como sinais direcionados a uma thread específica usando pthread_kill (3) ). Um sinal direcionado ao processo pode ser entregue a qualquer um dos threads que atualmente não tem o sinal bloqueado. Se mais de um dos encadeamentos tiver o sinal desbloqueado, o kernel escolhe um encadeamento arbitrário para o qual enviar o sinal.

Sarnold
fonte