Ressources informatiques

Ressources informatiques

Ressources informatiques

Créer des processus Linux

Documentation : perkamon.traduc.org : fork() wait() exec()

Cycle de vie d'un processus Duplication Duplication suivi d'un recouvrement

Cycle de vie d'un processus

Cycle de vie d'un processus

Duplication

Syntaxe des appels systèmes utilisés

Appel système fork()
#include <unistd.h>
pid_t fork(void);

Crée un nouveau processus par duplication du processus appelant. Le fils reçoit une copie de la zone mémoire du père.

Appel système wait()
#include  <sys/types.h>
#include  <sys/wait.h>
pid_t wait(int *status);

Attend la fin d'un fils.

Exemples

#include <unistd.h>     // pour fork()
#include <stdio.h>     // pour perror(), printf()
#include <sys/types.h>  // pour wait()
#include <sys/wait.h>   // pour wait()

int main() {
    int retourFork;

    // Creer un processus fils  
    retourFork = fork();
    if (retourFork == -1)
    {perror("fork :"); return -1;}

    // Dans le Fils  : la valeur de retour de fork() vaut 0.
    if (retourFork == 0) {
         printf("Fils : Je suis le fils\n");

         // Il est impératif de terminer le fils afin qu'il n'exécute
         // pas le code du père situé après l'accolade de fin du if.
         return 0;
    }

    // Dans le pere : la valeur de retour du fork est le PID du fils.
    printf("Père : Je suis le père. Mon fils a pour PID : %d\n", retourFork);

    // Attendre la fin du fils
    int codeFinFils;
    int retourWait;
    retourWait = wait(&codeFinFils);
    printf("Père : Mon fils de PID : %d vient de se terminer avec le code : %x\n",retourWait,codeFinFils);

    return 0;
}
Résultats d'exécutions
doe@debian:~/processus$ ./fork
Père : Je suis le père. Mon fils a pour PID : 28729
Fils : Je suis le fils
Père : Mon fils de PID : 28729 vient de se terminer avec le code : 0

J'ajoute une pause de 10 s avant le return 0 dans le fils. Cela me permet d'exécuter le programme fork en arrière plan à l'aide du & et de saisir la commande ps -f afin de visualiser les processus en cours d'exécution.
Le processus père et son PID sont surlignés en bleu, le processus fils et son PID sont surlignés en vert.

doe@debian:~/processus$ ./fork &
[1] 28887
doe@debian:~/processus$ Père : Je suis le père. Mon fils a pour PID : 28888
Fils : Je suis le fils.
ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
doe       2663 2579  0 16:43 pts/0    00:00:00 bash
doe      28887  2663  0 16:58 pts/0    00:00:00 ./fork
doe      28888 28887  0 16:58 pts/0    00:00:00 ./fork
doe      28906  2663  0 16:58 pts/0    00:00:00 ps -f
doe@debian:~/processus$ Père : Mon fils de PID : 28888 vient de se terminer avec le code : 0

J'envoie le signal SIGKILL de valeur 9 au fils

doe@debian:~/processus$ ./fork &
[1] 7550
doe@debian:~/processus$ Père : Je suis le père. Mon fils a pour PID : 7551
Fils : Je suis le fils.
kill -9 7551
doe@debian:~/processus$ Père : Mon fils de PID : 7551 vient de se terminer avec le code : 9

Je remplace l'instruction return 0 par return -1 dans le fils.

doe@debian:~/processus$ ./fork
Père : Je suis le père. Mon fils a pour PID : 7693
Fils : Je suis le fils.
Père : Mon fils de PID : 7693 vient de se terminer avec le code : ff00

Duplication suivi d'un recouvrement

Syntaxe

#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

Remplace le code du processus appelant par un nouveau code. Les fonctions execl() et execv() recherchent le nouveau programme à exécuter dans le répertoire courant, les fonctions execlp() et execvp() le recherchent dans le PATH.

Exemple avec l'appel système execl()

Le fils recouvre son code par celui de la commande ls -l.

#include  <unistd.h>     // pour fork(), exec_()
#include  <stdio.h>      // pour perror(), printf()
#include  <sys/types.h>  // pour wait()
#include  <sys/wait.h>   // pour wait()

int main() {
    int retourFork;

    // Creer un processus fils  
    retourFork = fork();
    if (retourFork == -1)
    {perror("fork :"); return -1;}

    // Dans le Fils  : la valeur de retour de fork() vaut 0.
    if (retourFork == 0) {
        // Recouvrir le code du fils avec celui d'un autre programme
        char *programme = (char *)"ls";
        char *nomProcessus = (char *)"ls";
        char *argument1 = (char *)"-l";
        execlp(programme, nomProcessus, argument1, NULL);
        // Si le code ci-dessous est exécuté c'est que la fonction execlp() a échoué
        perror("execl :");
        return -1;
    }

    // Dans le pere : la valeur de retour du fork est le PID du fils.
    printf("Père : Je suis le père. Mon fils a pour PID : %d.\n", retourFork);

    // Attendre la fin du fils
    int codeFinFils;
    int retourWait;
    retourWait = wait(&codeFinFils);
    printf("Père : Mon fils de PID : %d vient de se terminer avec le code : %x.\n",retourWait,codeFinFils);

    return 0;
}
Résultat d'exécution
doe@debian:~/processus/tempo$ ./exec
Père : Je suis le père. Mon fils a pour PID : 5583.
total 12
-rwxr-xr-x 1 doe doe 7496 sept. 19 08:34 exec
-rw-r--r-- 1 doe doe 1308 sept. 19 08:34 exec3.c
Père : Mon fils de PID : 5583 vient de se terminer avec le code : 0.

J'ai surligné en jaune le code exécuté par le processus fils.

Exemple avec l'appel système execvp()

Je remplace, dans l'exemple précédent, le code du fils par celui ci-dessous.

        // Recouvrir le code du fils avec celui d'un autre programme
        char *programme = (char *)"ls";
        char *nomProcessus = (char *)"ls";
        char *argument1 = (char *)"-l";
        char *arguments[3];
        arguments[0] = nomProcessus;
        arguments[1] = argument1;
        arguments[2] = NULL;

        execvp(programme, arguments);
        // Si le code ci-dessous est exécuté c'est que la fonction execvp() a échoué
        perror("execvp :");
        return -1;
Résultat d'exécution

Le résultat d'exécution est identique à celui du programme précédent.