Création et Terminaison de Processus

Process Creation & Termination

Lorsque nous exécutons un programme, un processus est créé et se termine après l’achèvement de l’exécution. Mais que faire si nous avons besoin de créer un processus au sein du programme et de planifier une tâche différente pour celui-ci ? Est-ce possible ? Oui, bien sûr, grâce à la création de processus. Après avoir effectué la tâche, le processus se terminera automatiquement ou vous pourrez le terminer selon vos besoins.

Création de Processus

La création de processus est réalisée grâce à l’appel système fork(). Le processus nouvellement créé est appelé le processus enfant et celui qui l’a initié (ou le processus lors du démarrage de l’exécution) est appelé le processus parent. Après l’appel système fork(), nous avons désormais deux processus : le processus parent et le processus enfant. Comment les différencier ? C’est très simple, grâce à leurs valeurs de retour.

Appel Système

Après la création du processus enfant, examinons les détails de l’appel système fork().

#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);

Cela crée le processus enfant. Après cet appel, il y a deux processus : celui existant, appelé le processus parent, et celui nouvellement créé, appelé le processus enfant.

L’appel système fork() retourne l’une des trois valeurs suivantes :

  • Une valeur négative pour indiquer une erreur, c’est-à-dire l’échec de création du processus enfant.
  • Zéro pour le processus enfant.
  • Une valeur positive pour le processus parent. Cette valeur est l’ID du processus du nouveau processus enfant créé.

Considérons un programme simple.

Nom du fichier : basicfork.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
    fork();
    printf("Appel de l'appel système fork()n");
    return 0;
}

Étapes d’Exécution

Compilation

gcc basicfork.c -o basicfork

Exécution/Résultat

Appel de l'appel système fork()
Appel de l'appel système fork()

Remarque : En général, après l’appel de fork(), le processus enfant et le processus parent effectuent des tâches différentes. Si la même tâche doit être exécutée, alors à chaque appel de fork(), elle sera exécutée 2 puissance n fois, où n est le nombre de fois où fork() est invoqué.

Dans le cas ci-dessus, fork() est appelé une fois, d’où la sortie est imprimée deux fois (2 puissance 1). Si fork() est appelé, par exemple, trois fois, alors la sortie sera imprimée huit fois (2 puissance 3). S’il est appelé cinq fois, alors elle sera imprimée 32 fois, et ainsi de suite.

Après avoir vu la création du processus enfant grâce à fork(), il est temps d’examiner les détails des processus parent et enfant.

Nom du fichier : pids_after_fork.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
    pid_t pid, mypid, myppid;
    pid = getpid();
    printf("Avant fork : l'identifiant du processus est %dn", pid);
    pid = fork();
    if (pid < 0) {
        perror("échec de fork()n");
        return 1;
    }
    // Processus enfant
    if (pid == 0) {
        printf("Ceci est le processus enfantn");
        mypid = getpid();
        myppid = getppid();
        printf("L'identifiant du processus est %d et l'ID du processus parent est %dn", mypid, myppid);
    } else {
        // Processus parent
        sleep(2);
        printf("Ceci est le processus parentn");
        mypid = getpid();
        myppid = getppid();
        printf("L'identifiant du processus est %d et l'ID du processus parent est %dn", mypid, myppid);
        printf("Le nouvel ID du processus créé ou pid de l'enfant est %dn", pid);
    }
    return 0;
}

Compilation et Étapes d’Exécution

Avant fork : l'identifiant du processus est 166629
Ceci est le processus enfant
L'identifiant du processus est 166630 et l'ID du processus parent est 166629
Avant fork : l'identifiant du processus est 166629
Ceci est le processus parent
L'identifiant du processus est 166629 et l'ID du processus parent est 166628
Le nouvel ID du processus créé ou pid de l'enfant est 166630

Un processus peut se terminer de deux manières :

  • De manière anormale, lorsque certaines signaux sont délivrés, par exemple le signal de terminaison.
  • Normalement, en utilisant l’appel système _exit() (ou _Exit()) ou la fonction de bibliothèque exit().

La différence entre _exit() et exit() réside principalement dans l’activité de nettoyage. exit() effectue certaines opérations de nettoyage avant de renvoyer le contrôle au noyau, tandis que _exit() (ou _Exit()) renverra immédiatement le contrôle au noyau.

Considérons le programme d’exemple suivant avec exit().

Nom du fichier : atexit_sample.c

#include <stdio.h>
#include <stdlib.h>
void exitfunc() {
    printf("Appel de la fonction de nettoyage - exitfunc()n");
    return;
}
int main() {
    atexit(exitfunc);
    printf("Bonjour, Monde !n");
    exit (0);
}

Compilation et Étapes d’Exécution

Bonjour, Monde !
Appel de la fonction de nettoyage - exitfunc()

Considérons le programme d’exemple suivant avec _exit().

Nom du fichier : at_exit_sample.c

#include <stdio.h>
#include <unistd.h>
void exitfunc() {
    printf("Appel de la fonction de nettoyage - exitfunc()n");
    return;
}
int main() {
    atexit(exitfunc);
    printf("Bonjour, Monde !n");
    _exit (0);
}

Compilation et Étapes d’Exécution

Bonjour, Monde !
Appel de la fonction de nettoyage - exitfunc()