8.4 KiB
Quelques exemples
Soit le programme suivant
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int i, n = 5;
pid_t childpid;
for (i=0; i<n; i++) {
if ((childpid = fork()) == -1) break;
if (childpid == 0)
printf("Processus %d avec pere %d, i = %d\n", getpid(), getppid(), i);
}
return EXIT_SUCCESS;
}
Dont voici une exécution
bash-4.4$ ./fork2
Processus 20276 avec pere 20274, i = 1
Processus 20275 avec pere 20274, i = 0
Processus 20278 avec pere 20275, i = 1
Processus 20277 avec pere 20274, i = 2
Processus 20279 avec pere 20276, i = 2
Processus 20280 avec pere 20274, i = 3
Processus 20281 avec pere 20275, i = 2
Processus 20282 avec pere 20278, i = 2
Processus 20283 avec pere 20277, i = 3
Processus 20284 avec pere 20276, i = 3
Processus 20286 avec pere 20279, i = 3
Processus 20287 avec pere 1, i = 4
Processus 20285 avec pere 1, i = 4
Processus 20288 avec pere 20275, i = 3
bash-4.4$ Processus 20290 avec pere 20278, i = 3
Processus 20289 avec pere 20281, i = 3
Processus 20292 avec pere 1, i = 4
Processus 20291 avec pere 1, i = 4
Processus 20294 avec pere 1, i = 4
Processus 20293 avec pere 20282, i = 3
Processus 20295 avec pere 1, i = 4
Processus 20296 avec pere 1, i = 4
Processus 20298 avec pere 1, i = 4
Processus 20299 avec pere 1, i = 4
Processus 20300 avec pere 1, i = 4
Processus 20297 avec pere 1, i = 4
Processus 20302 avec pere 1, i = 4
Processus 20301 avec pere 1, i = 4
Processus 20304 avec pere 1, i = 4
Processus 20303 avec pere 1, i = 4
Processus 20305 avec pere 1, i = 4
Les appels système wait(), waitpid() et exit()
Ces appels système permettent au processus père d’attendre la fin d’un de ses processus fils et de récupérer son status de fin. Ainsi, un processus peut synchroniser son exécution avec la fin de son processus fils en exécu- tant l’appel système wait():
int wait(int *status);
permet à un processus père d'attendre jusqu'à ce que le procesuss fils termine. La fonction retourne l'identifiant du processus fils à l'adresse de status
int waitpid(int pid, int *status, int options);
permet à un processus père d'attendre jusqu'à ce que le processus fils de numéro pid termine, il retourne l'identifiant du processus fils et son état de terminaison à l'adresse de status.
void exit(int return_code);
permet de finir volontairement l'exécution du processus et donne son état de terminaison. A noter qu'un processus peut terminer aussi par un arrêt forcé provoqié par un signal envoyé au processus
Le processus appelant est mis en attente jusqu’à ce que l’un de ses fils termine. Quand cela se produit, il revient de la fonction. Si status
est différent de 0, alors 16 bits d’information sont rangés dans les 16 bits de poids faible de l’entier pointé par status
. Ces informations permettent de savoir comment s’est terminé le processus selon les conventions suivantes :
- Si le fils est stoppé, les 8 bits de poids fort contiennent le numéro du signal qui a arrêté le processus et les 8 bits de poids faible ont la valeur octale
0177
. - Si le fils s’est terminé avec un
exit()
, les 8 bits de poids faible destatus
sont nuls et les 8 bits de poids fort contiennent les 8 bits de poids faible du paramètre utilisé par le processus fils lors de l’appel deexit()
. - Si le fils s’est terminé sur la réception d’un signal, les 8 bits de poids fort de ``status` sont nuls et les 7 bits de poids faible contiennent le numéro du signal qui a causé la fin du processus.
Dans le fichier d'en-tête <sys/wait.h>
sont définis différents macros
qui permettent d’analyser la valeur de status afin de determiner la cause de terminaison du processus. En particulier, le processus père peut obtenir la valeur des 8 bits de poids faible du paramètre qui reçoit depuis exit()
de la part du fils en utilisant le macro :
WEXITSTATUS(status)
/*
#define WEXITSTATUS(s) (((s)>>8)&0xFF)
*/
Si le processus appelant wait()
n'a pas de fils alors la fonction renvoie une erreur.
Exemple 1
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main() {
pid_t fils_pid ;
fils_pid = fork ();
if ( fils_pid == 0) {
printf("Je suis le fils avec pid %d\n",getpid()); exit(3);
} else {
if (fils_pid > 0) {
printf ("Je suis le pere avec pid %d.\n" , getpid ());
printf("J’attends que mon fils se termine\n");
wait (NULL) ;
printf("Normalement mon fils en a fini!\n");
} else {
printf("Erreur dans la creation du fils\n");
}
}
exit (0);
}
Dont une exécution est
bash-4.4$ ./wait
Je suis le pere avec pid 20892.
J’attends que mon fils se termine
Je suis le fils avec pid 20893
bash-4.4$
Exemple 2
/* fork2b.c */
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int i, n = 5;
pid_t childpid;
for (i=0; i<n; i++) {
if ((childpid = fork()) == -1) break;
if (childpid == 0)
printf("Processus %d avec pere %d, i = %d\n", getpid(), getppid(), i);
}
while (wait(NULL) >= 0)
;
return EXIT_SUCCESS;
}
Avec une exécution possible :
bash-4.4$ ./fork2b
Processus 21041 avec pere 21039, i = 1
Processus 21040 avec pere 21039, i = 0
Processus 21042 avec pere 21039, i = 2
Processus 21044 avec pere 21040, i = 1
Processus 21043 avec pere 21039, i = 3
Processus 21045 avec pere 21042, i = 3
Processus 21046 avec pere 21041, i = 2
Processus 21047 avec pere 21039, i = 4
Processus 21048 avec pere 21040, i = 2
Processus 21049 avec pere 21044, i = 2
Processus 21050 avec pere 21042, i = 4
Processus 21051 avec pere 21043, i = 4
Processus 21052 avec pere 21045, i = 4
Processus 21053 avec pere 21041, i = 3
Processus 21054 avec pere 21046, i = 3
Processus 21055 avec pere 21040, i = 3
Processus 21056 avec pere 21048, i = 3
Processus 21057 avec pere 21044, i = 3
Processus 21058 avec pere 21049, i = 3
Processus 21059 avec pere 21041, i = 4
Processus 21061 avec pere 21040, i = 4
Processus 21060 avec pere 21046, i = 4
Processus 21063 avec pere 21053, i = 4
Processus 21062 avec pere 21048, i = 4
Processus 21065 avec pere 21044, i = 4
Processus 21064 avec pere 21054, i = 4
Processus 21066 avec pere 21049, i = 4
Processus 21067 avec pere 21055, i = 4
Processus 21069 avec pere 21057, i = 4
Processus 21070 avec pere 21058, i = 4
Processus 21068 avec pere 21056, i = 4
bash-4.4$
Questions
1- Quelle est la valeur de retour de wait
si il n'y a pas de fils
2- Dessiner le graphe quand n = 3
, pour fork2b.c
.
3- Ecrire un programme qui nous permet de savoir si wait()
attend un ou tous les procesuss
Exemple 3
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void fils(int i);
int main() {
int status ;
pid_t pid;
pid = fork();
if (pid == -1) perror("fork");
else if (pid ==0) // creation du second fils
fils (1) ;
pid = fork();
if (pid == -1) perror("fork");
else if (pid ==0) // creation du second fils
fils (2) ;
if (wait(&status) > 0)
printf("fin du fils %d\n", WEXITSTATUS(status));
else perror("wait");
if (wait(&status) > 0)
printf("fin du fils %d\n", WEXITSTATUS(status));
else perror("wait");
if (wait(&status) > 0)
printf("fin du fils %d\n", WEXITSTATUS(status));
else perror("wait");
return 0 ;
}
void fils (int i) {
sleep(2);
exit(i);
}
Questions
1- Analyser le code précédent
2- Depuis le shell on peut envoyer un signal à un processus via kill
. Faire un programme C qui affiche le signal qui a été envoyé à un processus créé.
Processus zombie
L’exécution asynchrone entre processus parent et fils a certaines conséquences. Souvent, le fils d’un processus se termine, mais son père ne l’attend pas. Le processus fils devient alors un processus zombie. Le processus fils existe toujours dans la table des proces- sus, mais il n’utilise plus les ressources du kernel.
L’exécution de l’appel système wait()
ou waitpid()
par le processus père élimine le fils de la table des processus. Il peut y avoir aussi des terminaisons prématurées, où le processus parent se termine avant ses processus fils. Dans cette situation, ses processus fils sont adoptés par le processus init
, dont le pid = 1
.
Questions
1- Illustrer les processus zombies