Académique Documents
Professionnel Documents
Culture Documents
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.
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.
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.
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.
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
La modification de l’environnement :
◦ execve, execle
26