En général, pour tuer les processus, nous générons des signaux comme SIGKILL
, SIGTSTP
etc.
Mais comment sait-on qui a commandé ce signal particulier, qui l'a envoyé à un processus particulier et, en général, comment les signaux effectuent-ils leurs opérations? Comment fonctionnent les signaux en interne?
kill
signals
architecture
Varun Chhangani
la source
la source
Réponses:
La vue de 50 000 pieds est la suivante:
Un signal est généré soit par le noyau en interne (par exemple,
SIGSEGV
lorsqu'une adresse non valide est accédée, ouSIGQUIT
lorsque vous appuyez sur Ctrl+ \), soit par un programme utilisant l'kill
appel système (ou plusieurs associés).Si c'est par l'un des appels système, le noyau confirme que le processus appelant dispose de privilèges suffisants pour envoyer le signal. Sinon, une erreur est renvoyée (et le signal ne se produit pas).
S'il s'agit de l'un des deux signaux spéciaux, le noyau agit inconditionnellement sur lui, sans aucune entrée du processus cible. Les deux signaux spéciaux sont SIGKILL et SIGSTOP. Tous les éléments ci-dessous concernant les actions par défaut, les signaux de blocage, etc., ne sont pas pertinents pour ces deux.
Ensuite, le noyau comprend ce que fait le signal:
Pour chaque processus, une action est associée à chaque signal. Il y a un tas de valeurs par défaut et les programmes peuvent définir différents ceux utilisant
sigaction
,signal
etc. Ceux - ci comprennent des choses comme « l' ignorer complètement », « tuer le processus », « tuer le processus avec une décharge de base », « arrêter le processus », etc.Les programmes peuvent également désactiver la livraison de signaux ("bloqués"), signal par signal. Ensuite, le signal reste en attente jusqu'à ce qu'il soit débloqué.
Les programmes peuvent demander qu'au lieu que le noyau entreprenne lui-même une action, il envoie le signal au processus de manière synchrone (avec
sigwait
, et al. Ousignalfd
) ou de manière asynchrone (en interrompant ce que fait le processus et en appelant une fonction spécifiée).Il existe un deuxième ensemble de signaux appelés "signaux en temps réel", qui n'ont pas de signification spécifique, et permettent également la mise en file d'attente de plusieurs signaux (les signaux normaux n'attendent qu'un seul de chacun lorsque le signal est bloqué). Ils sont utilisés dans les programmes multithreads pour que les threads communiquent entre eux. Plusieurs sont utilisés dans l'implémentation des threads POSIX de la glibc, par exemple. Ils peuvent également être utilisés pour communiquer entre différents processus (par exemple, vous pouvez utiliser plusieurs signaux en temps réel pour qu'un programme fooctl envoie un message au démon foo).
Pour une vue de moins de 50 000 pieds, essayez
man 7 signal
également la documentation interne du noyau (ou source).la source
L'implémentation du signal est très complexe et est spécifique au noyau. En d'autres termes, différents noyaux implémenteront les signaux différemment. Une explication simplifiée est la suivante:
La CPU, basée sur une valeur de registre spéciale, a une adresse en mémoire où elle s'attend à trouver une "table descripteur d'interruption" qui est en fait une table vectorielle. Il existe un vecteur pour chaque exception possible, comme la division par zéro, ou trap, comme INT 3 (débogage). Lorsque le CPU rencontre l'exception, il enregistre les drapeaux et le pointeur d'instruction en cours sur la pile, puis saute à l'adresse spécifiée par le vecteur correspondant. Sous Linux, ce vecteur pointe toujours vers le noyau, où se trouve un gestionnaire d'exceptions. Le CPU est maintenant terminé et le noyau Linux prend le relais.
Notez que vous pouvez également déclencher une exception à partir du logiciel. Par exemple, l'utilisateur appuie sur CTRL-C , puis cet appel va au noyau qui appelle son propre gestionnaire d'exceptions. En général, il existe différentes manières d'accéder au gestionnaire, mais quelle que soit la même chose de base: le contexte est enregistré sur la pile et le gestionnaire d'exceptions du noyau est sauté.
Le gestionnaire d'exceptions décide ensuite quel thread doit recevoir le signal. Si quelque chose comme la division par zéro s'est produit, alors c'est facile: le thread qui a provoqué l'exception reçoit le signal, mais pour d'autres types de signaux, la décision peut être très complexe et dans certains cas inhabituels, un thread plus ou moins aléatoire pourrait obtenir le signal.
Pour envoyer le signal, ce que fait le noyau, c'est d'abord définir une valeur indiquant le type de signal,
SIGHUP
ou autre chose. Ce n'est qu'un entier. Chaque processus a une zone de mémoire "signal en attente" où cette valeur est stockée. Ensuite, le noyau crée une structure de données avec les informations de signal. Cette structure comprend une "disposition" de signal qui peut être par défaut, ignorer ou gérer. Le noyau appelle alors sa propre fonctiondo_signal()
. La phase suivante commence.do_signal()
décide d' abord si elle traitera le signal. Par exemple, si c'est une mise à mort , alorsdo_signal()
tue simplement le processus, fin de l'histoire. Sinon, il examine la disposition. Si la disposition est par défaut,do_signal()
traite le signal selon une politique par défaut qui dépend du signal. Si la disposition est gérée, cela signifie qu'il y a une fonction dans le programme utilisateur qui est conçue pour gérer le signal en question et le pointeur vers cette fonction sera dans la structure de données susmentionnée. Dans ce cas, do_signal () appelle une autre fonction du noyau,handle_signal()
, qui passe ensuite par le processus de retour en mode utilisateur et d'appel de cette fonction. Les détails de ce transfert sont extrêmement complexes. Ce code dans votre programme est généralement lié automatiquement à votre programme lorsque vous utilisez les fonctions designal.h
.En examinant correctement la valeur du signal en attente, le noyau peut déterminer si le processus gère tous les signaux, et prendra les mesures appropriées dans le cas contraire, ce qui pourrait mettre le processus en veille ou le tuer ou toute autre action, selon le signal.
la source
Bien que cette question ait été répondue, permettez-moi de publier un flux détaillé des événements dans le noyau Linux.
Ceci est entièrement copié à partir des publications Linux: Linux Signals - Internals sur le blog "Linux posts" à sklinuxblog.blogspot.in.
Programme Espace Utilisateur Signal C
Commençons par écrire un programme C de l'espace utilisateur de signal simple:
Ce code attribue un nouveau gestionnaire pour le signal SIGINT. SIGINT peut être envoyé au processus en cours en utilisant la combinaison de touches Ctrl+ C. Lorsque vous appuyez sur Ctrl+, Cle signal asynchrone SIGINT est envoyé à la tâche. Cela équivaut également à envoyer la
kill -INT <pid>
commande dans un autre terminal.Si vous faites un
kill -l
(c'est un minusculeL
, qui signifie «liste»), vous connaîtrez les différents signaux qui peuvent être envoyés à un processus en cours.La combinaison de touches suivante peut également être utilisée pour envoyer des signaux particuliers:
Si vous compilez et exécutez le programme C ci-dessus, vous obtiendrez la sortie suivante:
Même avec Ctrl+ Cou
kill -2 <pid>
le processus ne se terminera pas. Au lieu de cela, il exécutera le gestionnaire de signaux et reviendra.Comment le signal est envoyé au processus
Si nous voyons les internes du signal envoyés à un processus et mettons Jprobe avec dump_stack à la
__send_signal
fonction, nous verrons la trace d'appel suivante:La fonction principale appelle donc l'envoi du signal:
Maintenant, tout est mis en place et les modifications nécessaires sont apportées
task_struct
au processus.Traitement du signal
Le signal est vérifié / géré par un processus lorsqu'il revient d'un appel système ou si le retour d'une interruption est effectué. Le retour de l'appel système est présent dans le fichier
entry_64.S
.La fonction int_signal est appelée à partir de
entry_64.S
laquelle la fonction est appeléedo_notify_resume()
.Vérifions la fonction
do_notify_resume()
. Cette fonction vérifie si leTIF_SIGPENDING
drapeau est défini danstask_struct
:Appels et signaux SYSTEM
Les appels système «lents», par exemple bloquer la lecture / écriture, mettre les processus en attente:
TASK_INTERRUPTIBLE
ouTASK_UNINTERRUPTIBLE
.Une tâche en état
TASK_INTERRUPTIBLE
sera changée enTASK_RUNNING
état par un signal.TASK_RUNNING
signifie qu'un processus peut être planifié.S'il est exécuté, son gestionnaire de signaux sera exécuté avant la fin de l'appel système «lent». Le
syscall
ne se termine pas par défaut.Si l'
SA_RESTART
indicateur est défini,syscall
est redémarré une fois le gestionnaire de signaux terminé.Les références
la source
kill
commande, qui est un shell intégré). (2c) Si les points-virgules après la fermeture}
d'une fonction ne sont pas à proprement parler des erreurs, ils sont inutiles et peu orthodoxes. (3) Même si tout était correct, ce ne serait pas une très bonne réponse à la question. (3a) La question, quoique quelque peu floue, semble se concentrer sur la façon dont les acteurs (utilisateurs et processus) initient (c'est-à-dire envoient ) des signaux. … (Suite)