Sous Linux, que se passe-t-il lorsqu'un programme (qui a éventuellement plusieurs threads) reçoit un signal, comme SIGTERM ou SIGHUP?
Quel thread intercepte le signal? Plusieurs threads peuvent-ils obtenir le même signal? Existe-t-il un fil spécial entièrement dédié à la gestion des signaux? Sinon, que se passe-t-il dans le thread qui doit gérer le signal? Comment l'exécution reprend-elle après la fin de la routine du gestionnaire de signal?
Ceci est légèrement nuancé, en fonction de la version du noyau Linux que vous utilisez.
En supposant que les threads posix soient 2.6, et si vous parlez du système d’envoi qui envoie SIGTERM ou SIGHUP, le signal est envoyé au processus, qui est reçu et géré par le thread racine. À l'aide de threads POSIX, vous pouvez également envoyer SIGTERM à des threads individuels, mais je suppose que vous demandez ce qui se passe lorsque le système d'exploitation envoie le signal au processus.
Dans la version 2.6, SIGTERM entraîne la sortie "proprement" des threads enfants, alors qu'en 2.4, les threads ont été laissés dans un état indéterminé.
pthreads(7)
indique que POSIX.1 requiert tous les threads d'un attribut de partage de processus, notamment:
POSIX.1 requiert également que certains attributs soient distincts pour chaque thread, notamment:
masque de signal ( pthread_sigmask(3)
)
pile de signal alternative ( sigaltstack(2)
)
La routine complete_signal
du noyau Linux contient le bloc de code suivant - les commentaires sont très utiles:
/*
* 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.
*/
Donc, vous voyez que vous êtes responsable de l'endroit où les signaux sont délivrés:
Si votre processus a défini la disposition d'un signal sur SIG_IGN
Ou SIG_DFL
, Le signal est ignoré (ou par défaut - kill, core ou ignore) pour tous les threads.
Si votre processus a défini la disposition d'un signal sur une routine de gestionnaire spécifique, vous pouvez contrôler quel thread recevra les signaux en manipulant des masques de signal de thread spécifiques à l'aide de pthread_sigmask(3)
. Vous pouvez nommer un thread pour les gérer tous, ou créer un thread par signal, ou toute combinaison de ces options pour des signaux spécifiques, ou vous vous fiez au comportement par défaut actuel du noyau Linux consistant à transmettre le signal au thread principal.
Certains signaux, cependant, sont spéciaux en fonction de la page de manuel signal(7)
:
Un signal peut être généré (et donc en attente) pour un processus dans son ensemble (par exemple, lorsqu'il est envoyé avec kill (2) ) ou pour un thread spécifique (par exemple, certains signaux, tels que SIGSEGV et SIGFPE , générés à la suite de l’exécution d’une instruction spécifique en langage machine sont dirigés vers les threads, de même que les signaux destinés à un thread spécifique en utilisant pthread_kill (3) ). Un signal dirigé par le processus peut être envoyé à l'un des threads pour lesquels le signal n'est pas bloqué. Si le signal est débloqué dans plusieurs threads, le noyau choisit un thread arbitraire auquel le signal doit être transmis.