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
returnou 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.