Vous êtes sur la page 1sur 26

CHAPITRE 2 : PROCESSUS : CRÉATION ET SYNCHRONISATION PÈRE FILS

IMEN BOUAOUINA
◦ INTRODUCTION
◦ CRÉATION D’UN PROCESSUS
◦ SYNCHRONISATION ENTRE PÈRE ET FILS
◦ LES FONCTIONS DE RECOUVREMENT

2
INTRODUCTION
Un processus est un ensemble d'octets (en langage
machine) en cours d'exécution, en d'autres termes,
c'est l'exécution d'un programme.

Les processus sous Unix apportent :


◦ La multiplicité des exécutions. Plusieurs processus peuvent
être l'exécution d'un même programme.
◦ La protection des exécutions. Un processus ne peut
exécuter que ses instructions propres et ce de façon
séquentielle ; il ne peut pas exécuter des instructions
appartenant à un autre processus.

Les processus sous Unix communiquent entre eux et


avec le reste du monde grâce aux appels système.

4
CRÉATION D’UN PROCESSUS
Sous Unix, la création
d'un processus est
réalisée par l'appel
système fork(). Cet appel
système permet de créer
dynamiquement (en
cours d’exécution) un
nouveau processus qui
s’exécute de façon
concurrente avec le
processus qui l’a créé.

6
Le nouveau processus exécute le même code
que le processus parent et démarre comme
lui au retour du fork(). Le seul moyen de
distinguer le processus fils du processus père
est que la valeur de retour de la fonction fork
est 0 dans le processus fils créé et est égale
au numéro du processus fils nouvellement
créé, dans le processus père et -1 en cas
d’échec.

# include <unistd.h>
pid_t fork(void);

7
L’utilisation classique de la fonction fork est la suivante :

# include <unistd.h>
# include <stdio.h>
int pid;
pid = fork ( ) ;
if (pid == - 1)
{ /* code si échec : printf ( ” le fork ( ) a échoué \n ” ) */}
else
if (pid == 0)
{ /* code correspondant à l'exécution du processus fils */ }
else
{ /* code correspondant à l'exécution du processus père */ }

8
Exercice
Ecrire un programme "C" qui crée deux
processus (un père et son fils). Chacun d'eux
affichera à l'écran qui il est ("je suis le père de
PID : ??", je suis le fils de PID : ?? et de PID :
??).
Afin de connaître le PID (=numéro
d'identification d'un processus) du processus
en cours, la fonction pid_t getpid() s'impose.
Pour le processus père, c'est la commande
pid_t getppid().

9
SYNCHRONISATION PÈRE FILS
Les processus créés par des fork() s’exécutent de façon
concurrente avec leur père.

Ne pouvant présumer de la politique et de l’état de


l’ordonnanceur du système, il sera impossible de
déterminer quels processus se termineront avant tels
autres (y compris leur père).

Dans certains cas le père meurt avant là, on dira que ce


dernier est dans un état zombi (dans l’affichage de la
commande "ps", S=Z).

Le noyau rattache ces processus au processus init. D’où


l’existence, dans certains cas, d’un problème de
synchronisation à la terminaison du fils.

11
Primitives de synchronisation :
La primitive wait(int * pointeur_status)
◦ La primitive wait permet l’élimination des processus
zombis et la synchronisation d’un processus sur la
terminaison de ses descendants avec récupération des
informations relatives à cette terminaison.
◦ Elle provoque la suspension du processus appelant
jusqu’à ce que l’un de ses processus fils se termine.
◦ wait attend qu'un des enfants se termine (instruction
bloquante), l'identification de l'enfant est retournée
par la fonction

12
Le paramètre passé par adresse (int
*pointeur_status) permet d’obtenir des
informations sur la façon dont s’est terminé
le processus fils.
Cette information (sur 16bits) doit être
interprétée de la manière suivante:
◦ Si le processus fils se termine normalement par un
exit(k), alors l’octet de poids faible est mis à 0 et
l’octet de poids fort reçoit la valeur k.
◦ Si le processus se termine anormalement (signal),
les deux octets permettent d’obtenir le numéro de
ce signal (cf. man wait)...

13
14
La valeur de retour de wait est le numéro du
processus fils venant de se terminer.

Lorsqu'il n'y a plus (ou pas) de processus fils dont


il faut attendre la fin, la fonction wait renvoie -1.

Chaque fois qu'un fils se termine le processus père


sort de wait, et il peut consulter « etat » pour
obtenir des informations sur le fils qui vient de se
terminer.

15
La primitive waitpid((pid_t pid , int *
pointeur_status , int options)
◦ waitpid attend qu'un enfant spécifique se
termine (pid ), son comportement est
conditionné par la valeur de options
◦ le status pointeur_status peut être analysé
par des macro se trouvant dans <wait.h>

16
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait (int *pointeur_status)

#include <sys/types.h>
#include <sys/wait.h>
int waitpid (int pid, int *terminaison, int options)

#include<stdlib.h>
int exit()

17
La primitive exit()
◦ met fin à l'exécution d'un processus. Elle renvoie
au processus père un code de retour utilisable
par ce dernier.
◦ Les valeurs les plus communes : EXIT_FAILURE et
EXIT_SUCCESS

18
Lorsqu’un processus se termine (exit), le système
démantèle tout son context sauf l’entrée de la
table de processus le concernant.

Le processus père, par un wait, récupère la mort


de son fils, cumule les statistiques de celui-ci
avec les siennes et détruit l’entrée de la table des
processus concernant son fils défunt.

La communication entre le père et le fils se fait


par le biais d’un signal que le fils envoie au père
SIGHLD

19
Exercice

20
PRIMITIVES DE RECOUVREMENT
Lorsque nous appelons la fonction fork(), le
code du processus fils est exactement le même
que celui du processus parent.
Or, nous pouvons être amenés à avoir à
exécuter un autre programme dans le
processus fils.
Un ensemble de fonctions, dites « fonctions de
recouvrement » (ou primitives de
recouvrement), permettent de charger un autre
programme à la place de celui en cours.

22
Rappel:
int main (int argc, char* argv[], char* arge[])
◦ argc le nombre de composants de la commande
◦ argv un tableau de pointeurs de caractères donnant
accès aux différentes composantes de la commande
◦ arge un tableau de pointeurs de caractères donnant
accès à l’environnement du processus.
Exemple:
◦ calcul 3 4 argc = 3, argv[0]="calcul", argv[1]="3",
argv[2]="4"

23
Voici la liste de ces fonctions sous
Linux :
int execl (const char *path, const char *arg0, ...,
const char *argn, (char *)0);
int execle (const char *path, const char *arg0, ...,
const char *argn, (char *)0, char *const envp[]);
int execlp (const char *file, const char *arg0, ...,
const char *argn, (char *)0);
int execv (const char *path, char *const argv[]);
int execve (const char *path, char *const argv[], char
*const envp[]);
int execvp (const char *file, char *const argv[]);

24
Les fonctions de recouvrement ont toutes le
même rôle :
Permettre au processus de charger un
nouveau code exécutable à la place du code
qu’il a hérité de son père
Les primitives diffèrent par 3 manières :
◦ Passage des paramètres
◦ Chemin de l’exécutable à charger
◦ Modification de l’environnement

25
Passage de argV :
◦ Liste : execl, execlp, execle
◦ Tableau : execv, execvp, execve

Chemin de l’exécutable à charger:


◦ Utilisant la VE PATH : execlp et execvp
◦ Relativement au répertoire de travail pour les autres

La modification de l’environnement :
◦ execve, execle

26

Vous aimerez peut-être aussi