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
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.
- Valeur de retour :
- Le PID du fils dans le père
- 0 dans le fils
- -1 si le fils n'a pas été créé.
Appel système wait()
#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status);
Attend la fin d'un fils.
- *status : entier court apportant une indication sur la façon dont le fils s'est terminé :
- Le poids faible contient le numéro du signal reçu si le fils s'est terminé sur réception d'un signal.
- Le poids fort contient la valeur de retour du processus fils s'il s'est terminé par exécution de l'instruction
return
ou de la fonctionexit()
. - Valeur de retour :
- PID du fils qui vient de se terminer
- -1 en cas d'erreur
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
.
- const char *path : nom d'un fichier exécutable avec son chemin
- const char *file : nom d'un fichier exécutable se trouvant dans la liste des chemins des fichiers exécutable (variable
PATH
) - const char *arg, ... : liste variable d'arguments de la commande à exécuter dont le dernier est
NULL
- char *const argv[] : tableau d'arguments de la commande à exécuter dont le dernier est
NULL
- Valeur de retour : Ces fonctions retournent une valeur seulement si elles ont échouée et dans ce cas cette valeur est -1. En effet, lorsqu'elles réussissent, le code se trouvant en-dessous a été remplacé par un nouveau code.
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.