ASR31-valarche-2021/ASR31-3-Fork.md

8.4 KiB
Raw Blame History

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 dattendre la fin dun 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 lappel 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 lun de ses fils termine. Quand cela se produit, il revient de la fonction. Si status est différent de 0, alors 16 bits dinformation sont rangés dans les 16 bits de poids faible de lentier pointé par status. Ces informations permettent de savoir comment sest 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 sest terminé avec un exit(), les 8 bits de poids faible de status 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 lappel de exit().
  • Si le fils sest terminé sur la réception dun 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 danalyser 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("Jattends 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.
Jattends 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

Lexécution asynchrone entre processus parent et fils a certaines conséquences. Souvent, le fils dun processus se termine, mais son père ne lattend pas. Le processus fils devient alors un processus zombie. Le processus fils existe toujours dans la table des proces- sus, mais il nutilise plus les ressources du kernel.

Lexécution de lappel 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