Comment Linux "tue-t-il" un processus?

91

Cela me laisse souvent perplexe de constater que, même si je travaille professionnellement avec les ordinateurs depuis plusieurs décennies et Linux depuis une décennie, je traite en réalité la plupart des fonctionnalités du système d'exploitation comme une boîte noire, un peu comme la magie.

Aujourd'hui, j'ai pensé à la killcommande, et même si je l'utilise plusieurs fois par jour (tant par sa "normale" que par sa -9saveur), je dois avouer que je ne sais absolument pas comment elle fonctionne en arrière-plan.

De mon point de vue, si un processus en cours est "bloqué", j'appelle killson PID, puis il ne fonctionne plus du tout. La magie!

Qu'est-ce qui se passe vraiment là-bas? Les pages de manuel parlent de "signaux", mais ce n’est sûrement qu’une abstraction. L'envoi kill -9à un processus ne nécessite pas la coopération du processus (comme la gestion d'un signal), il le tue simplement.

  • Comment Linux empêche-t-il le processus de continuer à utiliser son temps processeur?
  • Est-il retiré de la planification?
  • Déconnecte-t-il le processus de ses handles de fichiers ouverts?
  • Comment la mémoire virtuelle du processus est-elle libérée?
  • Y a-t-il quelque chose comme une table globale en mémoire, où Linux garde des références à toutes les ressources occupées par un processus, et quand je "tue" un processus, Linux parcourt simplement cette table et libère les ressources une par une?

J'aimerais vraiment savoir tout ça!

sératoninant
la source
Cette réponse à une question sur SIGKILL pourrait également être pertinente ici.
telcoM

Réponses:

72

L'envoi de kill -9 à un processus n'exige pas la coopération du processus (comme la gestion d'un signal), il le tue simplement.

Vous présumez que parce que certains signaux peuvent être captés et ignorés, ils impliquent tous une coopération. Mais, comme indiqué man 2 signal, "les signaux SIGKILL et SIGSTOP ne peuvent être ni capturés ni ignorés". SIGTERM peut être intercepté, raison pour laquelle plain killn'est pas toujours efficace - cela signifie généralement que quelque chose a mal tourné dans le gestionnaire de processus. 1

Si un processus ne définit pas (ou ne peut pas) définir un gestionnaire pour un signal donné, le noyau effectue une action par défaut. Dans le cas de SIGTERM et SIGKILL, il s’agit de mettre fin au processus (sauf si son PID est 1; le noyau ne se terminera pas init) 2, ce qui signifie que ses descripteurs de fichier sont fermés, sa mémoire est renvoyée dans le pool système, son parent reçoit SIGCHILD, son orphelin. les enfants sont hérités par init, etc., comme si elle avait appelé exit(voir man 2 exit). Le processus n'existe plus - à moins qu'il ne devienne un zombie, auquel cas il est toujours répertorié dans la table des processus du noyau avec certaines informations; cela se produit lorsque son parent newaitet traiter correctement ces informations. Cependant, les processus zombies ne disposent plus de mémoire et ne peuvent donc pas continuer à s'exécuter.

Y a-t-il quelque chose comme une table globale en mémoire où Linux garde des références à toutes les ressources occupées par un processus et quand je "tue" un processus, Linux passe simplement par cette table et libère les ressources une par une?

Je pense que c'est assez précis. La mémoire physique est suivie par page (une page correspond généralement à un bloc de 4 Ko) et ces pages sont extraites et renvoyées dans un pool global. C'est un peu plus compliqué en ce sens que certaines pages libérées sont mises en cache au cas où les données qu'elles contiennent seraient à nouveau requises (c'est-à-dire des données qui ont été lues à partir d'un fichier existant).

Les pages de manuel parlent de "signaux" mais ce n’est sûrement qu’une abstraction.

Bien sûr, tous les signaux sont une abstraction. Ils sont conceptuels, tout comme les "processus". Je joue un peu la sémantique, mais si vous voulez dire que SIGKILL est qualitativement différent de SIGTERM, alors oui et non. Oui dans le sens où cela ne peut pas être attrapé, mais non dans le sens où ce sont deux signaux. Par analogie, une pomme n'est pas une orange, mais les pommes et les oranges sont, selon une définition préconçue, les deux fruits. SIGKILL semble plus abstrait puisque vous ne pouvez pas l'attraper, mais c'est toujours un signal. Voici un exemple de traitement par SIGTERM. Je suis sûr que vous avez déjà vu ces exemples:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

void sighandler (int signum, siginfo_t *info, void *context) {
    fprintf (
        stderr,
        "Received %d from pid %u, uid %u.\n",
        info->si_signo,
        info->si_pid,
        info->si_uid
    );
}

int main (void) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = sighandler;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGTERM, &sa, NULL);
    while (1) sleep(10);
    return 0;
}

Ce processus ne fera que dormir pour toujours. Vous pouvez l'exécuter dans un terminal et l'envoyer avec SIGTERM kill. Il crache des choses comme:

Received 15 from pid 25331, uid 1066.

1066 est mon UID. Le PID sera celui du shell à partir duquel killest exécuté, ou le PID de kill si vous le branchez ( kill 25309 & echo $?).

Là encore, il est inutile de définir un gestionnaire pour SIGKILL car cela ne fonctionnera pas. 3 Si kill -9 25309le processus se termine. Mais c'est toujours un signal. le noyau a l'information sur qui a envoyé le signal , de quel type de signal il s'agit, etc.


1. Si vous n'avez pas consulté la liste des signaux possibles , voir kill -l.

2. Une autre exception, comme Tim Post le mentionne ci-dessous, s’applique aux processus en sommeil ininterruptible . Ceux-ci ne peuvent pas être réveillés tant que le problème sous-jacent n'est pas résolu. Par conséquent, TOUS les signaux (y compris SIGKILL) sont différés pour la durée. Un processus ne peut toutefois pas créer cette situation exprès.

3. Cela ne signifie pas que l’utilisation kill -9est une meilleure chose à faire en pratique. Mon exemple de gestionnaire est mauvais en ce sens qu'il ne mène pas à exit(). Le véritable objectif d’un gestionnaire SIGTERM est de donner au processus la possibilité de nettoyer, par exemple, des fichiers temporaires, puis de le quitter volontairement. Si vous l'utilisez kill -9, il n'a pas cette chance, alors ne le faites que si la partie "exit volontairement" semble avoir échoué.

boucle d'or
la source
Ok mais avec quoi tuer le processus -9parce que c’est le vrai problème, qui veut que celui-ci meure! ;)
Kiwy
@ Kiwy: Le noyau. L'IPC, y compris les signaux, le traverse; le noyau implémente les actions par défaut.
goldilocks
12
Peut-être vaut-il la peine de mentionner que la veille du disque (D) préempte tous les signaux, tant que le processus est dans cet état. Par conséquent, essayer de kill -9certains processus liés aux E / S ne va pas marcher, du moins pas tout de suite.
Tim Post
7
J'ajouterais que kill -9, dans la mesure où un processus qui ne peut être capturé, un processus le recevant ne peut effectuer aucun nettoyage (par exemple, supprimer des fichiers temporaires, libérer de la mémoire partagée, etc.) avant de se fermer. Par conséquent, utilisez kill -9(aka kill -kill) seulement en dernier recours. Commencez par a kill -hupet / ou d' kill -termabord, puis utilisez-le kill -killcomme coup de grâce.
JRFerguson
"Le processus n'existe plus - à moins qu'il ne devienne un zombie, il est toujours répertorié dans la table des processus du noyau avec quelques informations". En fait, tous les processus passent à l'état de zombie lorsqu'ils meurent et le zombie disparaît le parent fait un waitpid sur l'enfant, normalement cela se produit trop vite pour que vous le voyiez arriver
malin
3

Chaque processus s'exécute à l'heure programmée puis est interrompu par une minuterie matérielle pour donner le cœur de son processeur à d'autres tâches. C'est pourquoi il est possible d'avoir beaucoup plus de processus qu'il n'y a de cœurs de processeur, ou même d'exécuter tous les systèmes d'exploitation avec beaucoup de processus sur un seul processeur.

Une fois le processus interrompu, le contrôle revient au code du noyau. Ce code peut alors prendre la décision de ne pas reprendre l'exécution du processus interrompu, sans aucune coopération de la part du processus. kill -9 peut finir par être exécuté dans n’importe quelle ligne de votre programme.

h22
la source
0

Voici une description idéalisée du fonctionnement d'un processus. En pratique, toute variante Unix comportera de nombreuses complications et optimisations supplémentaires.

Le noyau a une structure de données pour chaque processus qui stocke des informations sur la mémoire qu'il mappe, ses threads et son calendrier, les fichiers ouverts, etc. Si le noyau décide de supprimer un processus, il note la structure de données du processus (et peut-être dans la structure de données de chacun des threads) que le processus doit être tué.

Si l'un des threads du processus est actuellement planifié sur un autre processeur, le noyau peut déclencher une interruption sur cet autre CPU pour que ce thread cesse de s'exécuter plus rapidement.

Lorsque le planificateur constate qu'un thread est dans un processus qui doit être tué, il ne le planifie plus.

Lorsque plus aucun thread du processus n'est planifié, le noyau commence à libérer les ressources du processus (mémoire, descripteurs de fichier,…). Chaque fois que le noyau libère une ressource, il vérifie si son propriétaire dispose toujours de ressources actives. Une fois que le processus n'a plus aucune ressource active (mappage de la mémoire, descripteur de fichier ouvert,…), la structure de données du processus lui-même peut être libérée et l'entrée correspondante peut être supprimée de la table des processus.

Certaines ressources peuvent être libérées immédiatement (par exemple, désallocation de mémoire qui n'est pas utilisée par une opération d'E / S). D'autres ressources doivent attendre, par exemple, les données décrivant une opération d'E / S ne peuvent pas être libérées tant que l'opération est en cours (pendant qu'un DMA est en cours, la mémoire à laquelle il accède est utilisée et l'annulation de ce dernier nécessite contacter le périphérique). Le conducteur d'une telle ressource est notifié et peut tenter de hâter l'annulation; une fois que l'opération n'est plus en cours, le pilote termine la libération de cette ressource.

(L'entrée dans la table de processus est en fait une ressource appartenant au processus parent. Elle est libérée à la fin du processus et lorsque le parent reconnaît l'événement .)

Gilles
la source