GNU/Linux >> Tutoriels Linux >  >> Linux

Que se passe-t-il lors de l'envoi de SIGKILL à un processus zombie sous Linux ?

Pour répondre à cette question, vous devez comprendre comment les signaux sont envoyés à un processus et comment un processus existe dans le noyau.

Chaque processus est représenté par un task_struct à l'intérieur du noyau (la définition est dans le sched.h fichier d'en-tête et commence ici). Cette structure contient des informations sur le processus; par exemple le pid. Les informations importantes se trouvent à la ligne 1566 où le signal associé est stocké. Ceci n'est défini que si un signal est envoyé au processus.

Un processus mort ou un processus zombie a toujours un task_struct . La structure reste, jusqu'à ce que le processus parent (naturel ou par adoption) ait appelé wait() après avoir reçu SIGCHLD pour récolter son processus enfant. Lorsqu'un signal est envoyé, le signal_struct est défini. Peu importe si le signal est captable ou non, dans ce cas.

Les signaux sont évalués à chaque fois que le processus s'exécute. Ou pour être exact, avant le processus serait Cours. Le processus est alors dans le TASK_RUNNING Etat. Le noyau exécute le schedule() routine qui détermine le prochain processus en cours d'exécution en fonction de son algorithme d'ordonnancement. En supposant que ce processus est le prochain processus en cours d'exécution, la valeur de signal_struct est évalué, qu'il y ait ou non un signal en attente à traiter. Si un gestionnaire de signal est défini manuellement (via signal() ou sigaction() ), la fonction enregistrée est exécutée, sinon l'action par défaut du signal est exécuté. L'action par défaut dépend du signal envoyé.

Par exemple, le SIGSTOP le gestionnaire par défaut du signal changera l'état du processus actuel en TASK_STOPPED puis exécutez schedule() pour sélectionner un nouveau processus à exécuter. Remarquez, SIGSTOP n'est pas attrapable (comme SIGKILL ), il n'est donc pas possible d'enregistrer un gestionnaire de signal manuel. En cas de signal non attrapable, l'action par défaut sera toujours exécutée.

À votre question :

Un processus défunt ou mort ne sera jamais déterminé par le planificateur comme étant dans le TASK_RUNNING déclarer à nouveau. Ainsi, le noyau n'exécutera jamais le gestionnaire de signal (par défaut ou défini) pour le signal correspondant, quel que soit le signal. Donc le exit_signal ne sera plus jamais fixé. Le signal est "livré" au processus en définissant le signal_struct en task_struct du processus, mais rien d'autre ne se produira, car le processus ne s'exécutera plus jamais. Il n'y a pas de code à exécuter, tout ce qui reste du processus est cette structure de processus.

Cependant, si le processus parent récupère ses enfants par wait() , le code de sortie qu'il reçoit est celui lorsque le processus est "initialement" mort. Peu importe s'il y a un signal en attente de traitement.


Un processus zombie est fondamentalement déjà mort. La seule chose est que personne n'a encore reconnu sa mort, donc il continue d'occuper une entrée dans la table des processus ainsi qu'un bloc de contrôle (la structure que le noyau Linux maintient pour chaque thread en activité). D'autres ressources telles que les verrous obligatoires sur les fichiers, les segments de mémoire partagée, les sémaphores, etc. sont récupérées.

Vous ne pouvez pas les signaler car personne ne peut agir sur ce signal. Même les signaux fatals comme KILL sont inutiles puisque le processus a déjà terminé son exécution. Vous pouvez essayer vous-même :

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
    pid_t pid = fork();

    if (pid == -1)
        exit(-1);

    if (pid > 0) {
        //parent
        printf("[parent]: I'm the parent, the pid of my child is %i\n"
            "I'll start waiting for it in 10 seconds.\n", pid);
        sleep(10);
        int status;
        wait(&status);

        if (WIFSIGNALED(status)) {
            printf("[parent]: My child has died from a signal: %i\n", WTERMSIG(status));
        } else if (WIFEXITED(status)) {
            printf("[parent]: My child has died from natural death\n");
        } else {
            printf("[parent]: I don't know what happened to my child\n");
        }
    } else {
        //child
        printf("[child]: I'm dying soon, try to kill me.\n");
        sleep(5);
        printf("[child]: Dying now!\n");
    }

    return 0;
}

Ici, je démarre un processus qui bifurque et dort avant d'attendre son enfant. L'enfant ne fait que dormir un peu. Vous pouvez tuer l'enfant pendant qu'il dort ou juste après qu'il soit sorti pour voir la différence :

$ make zombie 
cc     zombie.c   -o zombie

$ ./zombie    
[parent]: I'm the parent, the pid of my child is 16693
I'll start waiting for it in 10 seconds.
[child]: I'm dying soon, try to kill me.
# Here, I did "kill -15 16693" in another console
[parent]: My child has died from a signal: 15

$ ./zombie
[parent]: I'm the parent, the pid of my child is 16717
I'll start waiting for it in 10 seconds.
[child]: I'm dying soon, try to kill me.
[child]: Dying now!
# Here, I did "kill -15 16717" in another console
[parent]: My child has died from natural death

Linux
  1. UNIX / Linux :3 façons d'envoyer un signal aux processus

  2. Fondamentaux des signaux Linux - Partie I

  3. Linux :trouver et tuer les processus zombies

  4. Que faire lorsque Ctrl + C ne peut pas tuer un processus ?

  5. Quelle est la définition d'une session sous Linux?

Comment tuer un processus zombie sous Linux

Comment tuer les processus Zombie sous Linux

Linux - Que faire lorsqu'un bureau Linux se fige ?

SIGTERM vs SIGKILL :Quelle est la différence ?

Qu'est-ce qu'un processus arrêté sous Linux ?

Qu'advient-il d'un processus Linux multithread s'il reçoit un signal ?