Les processus zombies

I. Présentation

Au cours de leurs échanges avec le système et les programmes, les processus sont amenés à modifier leur état pour indiquer leur disponibilité. Ces changements sont le plus souvent dus à un besoin en ressources mémoire ou matérielle, à l'écriture de données ou encore à une attente (comme une action utilisateur).

Les états les plus connus sont l'état R (en cours d’exécution), S (en sommeil), T (stoppé) ou encore Z (zombie). Ce dernier est particulier, car il désigne un processus qui, bien qu'ayant terminé son exécution, reste présent sur le système, en attente d'être pris en compte par son père.

II. Comment les processus zombies apparaissent ?

Quand un processus se termine normalement, le système désalloue les ressources qui lui avaient été attribuées (code, données, pile d’exécution) tout en conservant son bloc de contrôle. Le système va ensuite attribuer l'état TASK_ZOMBIE au processus fils, qui se traduira par l'état Z (Zombie) que l'on peut observer avec la commande ps. Le processus père sera ensuite prévenu, à l'aide du signal SIGCHLD, que son fils vient de finir sa tâche.

A. Les processus zombies contrôlés

Lorsque le système envoie le signal SIGCHLD au processus père, ce dernier va récupérer, à l'aide des primitives wait() ou waitpid(), le code de retour de son fils terminé. Le père cumulera alors les statistiques de son fils avec les siennes et supprimera son entrée de la table des processus, le fils pourra alors totalement être effacé du système.

En temps normal, l'état zombie d'un processus ne pose aucun problème sur le système tant que le programme a été pensé pour que le père puisse réceptionner l'état de ses fils terminés.

B. Les processus zombies errants

Si le processus père n'a pas été conçu pour réceptionner le code de retour de chaque processus fils qu'il crée, ces derniers resteront à l’état zombies pendant toute sa durée d'exécution, ce qui peut être problématique si le père engendre à intervalle régulier des fils et s'il n'a pas été conçu pour être arrêté (un serveur, une tâche de fond, ...).

Sans cette prise en compte les processus fils zombies disposeront toujours d'un PID et occuperont la table des processus et resteront ainsi présent sur le système.

C. Comment se débarrasser des processus zombies ?

On ne peut pas… Ils sont déjà morts… La commande kill n'a aucun effet sur eux.

Le seul recours possible est de directement mettre un terme au processus père, avec par exemple la commande kill. Les processus fils zombies seront alors adoptés par init et ce dernier se chargera de les supprimer de la table des processus.

On pourrait penser, à tort, que les processus zombies ne sont pas gênant en soit puisqu'ils ne consomment aucune ressource et qu'ils ont terminé leur tâche. Le problème est que la quantité de processus qu'un système peut créer est limitée et un trop grand nombre de mauvaises synchronisations entre pères et fils entraînera à terme une saturation de la table des processus et bloquera tout le système qui ne pourra plus en créer de nouveaux.

IV. Comment créer une invasion zombie ?

Si vous aimez The Walking Dead je vous propose un script en C qui permet d'étudier les processus Zombies. C'est un script très simple, conçu exprès pour facilement les observer avec l'aide de la commande ps -aux, à lancer dans un autre terminal à intervalles réguliers.

A. Script en C pour créer des processus zombies

Ce script permet de générer des processus zombies, il est possible, entre autres, de paramétrer le nombre de zombies à créer et leur durée de vie (temps d'attente).

#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>

main()
{
        int pid;
        int i = 1;
        int nbre  = 5;   //** Nombre de zombies à créer 
        int vcrea = 2;   //** Vitesse de création des zombies, en seconde
        int vdest = 5;   //** Vitesse de destruction des zombies, en seconde
        int tpsZ  = 20;  //** Temps d'attente pour observer les zombies, en seconde
        int tpsP  = 5;   //** Temps d'attente pour observer le père, en seconde

        printf ("------------------------------------------------------------------------\n");
        printf ("--                     Lancement de l'invasion                        --\n");
        printf ("------------------------------------------------------------------------\n");

        for (i; i <= nbre; i++)
        {
                pid = fork();

                if (pid == 0) // processus fils
                {
                        printf("* Zombie %d dit : Ceeeervau.....\n", i);
                        exit(1);
                }
                else // processus pere
                {
                        sleep(vcrea);
                }
        }

        printf ("------------------------------------------------------------------------\n"     );
        printf ("  Vous avez %d processus zombies qui se baladent sur votre système.     \n", i-1);
        printf ("  Ils vont errer pendant %d sec, vous pouvez les observer avec ps -aux. \n",tpsZ);
        printf ("------------------------------------------------------------------------\n"     );

        // Temps d'attente, en seconde, pour observer les zombies
        sleep(tpsZ);

        // Le père fait ensuite appel à wait() et récupère ces fils terminés
        for (i; i > 1; i--)
        {
                sleep(vdest);
                wait(0);
                printf("* Le zombie %d a disparu \n", i-1);
        }

        printf ("------------------------------------------------------------------------\n");
        printf ("  L'invasion zombie est terminé.\n"                                        );
        printf ("  Le processus père est encore observable %d secondes.\n", tpsP            );
        printf ("------------------------------------------------------------------------\n");

        // Le père reste encore x secondes pour l'observer
        sleep(tpsP);


        // Fin du script, le père se termine

}

La primitive fork() permet de créer un processus fils, le PID du processus fils est envoyé au père et la valeur 0 est envoyé au processus enfant. La primitive exit() quand à elle met fin à un processus et le système va désallouer les ressources auparavant attribuées, sauf l'entrée dans la table des processus. Enfin, l'appel à wait() permet au processus père d'attendre et de récupérer la terminaison de son fil via le signal SIGCHLD envoyé par le système lors de la terminaison d'un processus.

B. Compilation

Pour compiler la source du script, intitulé ici ZombieInvader.c, vous pouvez utiliser cette commande :

gcc -o ZombieInvader ZombieInvader.c

Pour le lancer à partir d'un terminal :

./ZombieInvader

Vous devriez voir ceci :

------------------------------------------------------------------------
-- Lancement de l'invasion --
------------------------------------------------------------------------
* Zombie 1 dit : Ceeeervau.....
* Zombie 2 dit : Ceeeervau.....
* Zombie 3 dit : Ceeeervau.....
* Zombie 4 dit : Ceeeervau.....
* Zombie 5 dit : Ceeeervau.....
------------------------------------------------------------------------
Vous avez 5 processus zombies qui se baladent sur votre système.
Ils vont errer pendant 20 sec, vous pouvez les observer avec ps -aux.
------------------------------------------------------------------------

Après 20 sec, le script fera appel à wait() et les processus zombies seront supprimés.

C. Observation avec ps

En lançant, dans un autre terminal, la commande ps -aux au cours de l’exécution du script, on peut observer le statut Z des 5 processus enfants ainsi que l'annotation <defunct> en fin de ligne.

Ps -aux

F  UID   PID   PPID  PRI  NI  VSZ    RSS   WCHAN   STAT  TTY     TIME    COMMAND
[...]
0  1000  4974  3738  20   0   4200   672   hrtime  S+    pts/0   0:00    ./ZombieInvader 
1  1000  4975  4974  20   0      0     0   -       Z+    pts/0   0:00    [ZombieInvader] <defunct>
1  1000  4976  4974  20   0      0     0   -       Z+    pts/0   0:00    [ZombieInvader] <defunct>
1  1000  4977  4974  20   0      0     0   -       Z+    pts/0   0:00    [ZombieInvader] <defunct>
1  1000  4978  4974  20   0      0     0   -       Z+    pts/0   0:00    [ZombieInvader] <defunct>
1  1000  4979  4974  20   0      0     0   -       Z+    pts/0   0:00    [ZombieInvader] <defunct>
[...]

Il est aussi possible avec les options axjf de voir les processus de façon arborescente :

Ps -axjf

PPID  PID   PGID  SID   TTY     TPGID STAT UID   TIME  COMMAND
[...]
1611  3728  1363  1363  ?         -1  Rl   1000  0:54  \_ gnome-terminal
3728  3737  1363  1363  ?         -1  S    1000  0:00     \_ gnome-pty-helper
3728  3738  3738  3738  pts/0   4974  Ss   1000  0:00     \_ bash
3738  4974  4974  3738  pts/0   4974  S+   1000  0:00     |  \_ ./ZombieInvader
4974  4975  4974  3738  pts/0   4974  Z+   1000  0:00     |    \_ [ZombieInvader] <defunct>
4974  4976  4974  3738  pts/0   4974  Z+   1000  0:00     |    \_ [ZombieInvader] <defunct>
4974  4977  4974  3738  pts/0   4974  Z+   1000  0:00     |    \_ [ZombieInvader] <defunct>
4974  4978  4974  3738  pts/0   4974  Z+   1000  0:00     |    \_ [ZombieInvader] <defunct>
4974  4979  4974  3738  pts/0   4974  Z+   1000  0:00     |    \_ [ZombieInvader] <defunct>
3728  3755  3755  3755  pts/12  4373  Ss   1000  0:00     \_ bash
3755  4373  4373  3755  pts/12  4373  S+   1000  0:01     |   \_ vim ZombieInvader.c
3728  3880  3880  3880  pts/19  4980  Ss   1000  0:00     \_ bash
3880  4980  4980  3880  pts/19  4980  R+   1000  0:00     |   \_ ps -axjf
[...]

Ces deux commandes peuvent être lancées à tous moments et plusieurs fois lors de l’exécution du script. On remarquera aussi que les processus fils ont bien tous le même PPID (ici 4974), soit le PID le leur père.

Partagez cet article Partager sur Twitter Partager sur Facebook Partager sur Linkedin Partager sur Google+ Envoyer par mail

Aline

Administratrice réseaux et systèmes (Linux). Entre deux maintenances serveur, j'écris des articles dans le but de partager mon expérience professionnelle sur Linux, les réseaux informatiques, la sécurité et le scripting.

Nombre de posts de cet auteur : 4.Voir tous les posts

4 thoughts on “Les processus zombies

      • l’état R n’est pas uniquement l’état d’un processus en cours d’exécution mais aussi et surtout les nombreux processus qui sont prêt et qui sont en attente de la prise du processeur.

        Répondre
  • Bonjour

    Petit remarque.
    La commande « ps » c’est du linux.
    Et l’article est classé dans : Administration Systèmes » Windows Server » Système » Les processus zombies .

    En tout cas bon cas pratique, tout se que j’aime =)

    Répondre

Répondre à Florian BURNEL Annuler la réponse

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.