Vous êtes sur la page 1sur 7

Comprendre les processus en C sous linux

Programmation Système - Fork

Introduction
Un fork est une fonctionnalité sous les systèmes Unix ou Linux qui permet de dupliquer un processus. Pour
expliquer son fonctionnement, on va partir d'un processus qu'on appellera avec affection "Le Papa". Ce
processus va simplement se dupliquer et les deux processus (le père et le fils) affichent chacun leur statut
(père ou fils).

Lancement du père
Nous allons partir d'un processus père. Afin de bien déterminer les
enjeux du fork, on va également observer ses descripteurs de
fichiers, ou plutôt l'un des plus importants: celui du flux de sortie
(stdout) c'est-à-dire l'écran. On va aussi lui mettre une petite variable
globale qui servira plus tard pour l'affichage du statut du processus
(père ou fils).

Voici à quoi va ressembler notre processus de départ:

La flèche rouge pointe vers la prochaine instruction à exécuter.


Comme on n'a encore rien exécuté, on en est au début du main. On
va donc exécuter les deux premières instructions:

On peut voir en rouge les instructions qui ont été exécutées, ainsi
que les données qui ont été modifiées par la dernière instruction.
Jusqu'ici tout va bien, on a changé la valeur de quisuisje pour lui
attribuer l'identité du père. Passons à l'instruction suivante.

Le fork
La prochaine instruction est la plus délicate à comprendre,
exécutons-la et regardons ce qui se passe.
Le père a appelé fork et il s'est dupliqué. Cela implique plusieurs choses:

● Un nouveau processus a été créé, il est considéré comme le fils du processus qui a appelé fork()
● Ce processus est une copie conforme de son père. D'ailleurs, la prochaine instruction à exécuter
sera la même pour les deux : la condition if.
● La fonction fork() ne retourne pas la même chose pour les deux processus. Pour le fils, il
retournera 0. Pour le père, il retourne le pid du fils (son numéro de processus).
● Cette duplication implique certaines choses concernant les variables et les descripteurs de
fichiers. Nous allons y venir.

Passons à l'instruction suivante


pour les deux.

Maîtriser le fil d'exécution du


père et celui du fils

Les deux processus viennent de


vérifier la condition if. Etant donné
que chez le père, la variable pid est
différente de 0, il continuera dans
le else. Par contre, le fils entrera
dans le bloc du if car, pour lui, pid
est égal à 0.

Important: C'est donc ainsi qu'on


maîtrise le fil d'exécution du père
et celui du fils: en vérifiant la
valeur de la variable pid qui est
différente pour les deux.

On continue.
Les variables et les descripteurs de fichiers

Attention ici c'est un point à ne pas


manquer!

-Le fils a changé la valeur de sa variable


quisuisje. Ceci a changé la valeur de sa
propre variable quisuisje, mais pas celle du
père. Voici donc notre première
conclusion: les variables du père et celles
du fils sont totalement distinctes ; même si
elles portent le même nom, il ne s'agit pas
des mêmes variables. En revanche, vous
aurez remarqué qu'au moment du fork, le
fils avait hérité des valeurs de toutes les
variables de son père.

-Le père vient de faire un printf et a donc


écrit "Je suis Le pere" sur le flux de sortie
standard (stdout). Donc, après cette
écriture, le pointeur du fichier stdout a
avancé de 15 caractères (la longueur de la
phrase affichée). Avez-vous remarqué qu'il
en était de même chez le fils? En effet, si le
père et le fils ont des variables distinctes, en revanche, leur descripteurs de fichiers sont les mêmes. Donc,
si l'un des deux processus modifie son pointeur de position dans un fichier, ça se répercutera également
chez l'autre. Attention, cela ne vaut que pour les descripteurs de fichiers hérités durant le fork. Si le père ou
le fils ouvre d'autres fichiers après le fork, ces descripteurs ne seront pas partagés entre eux deux. De
même, si le fils ferme un descripteur de fichier hérité du père, le descripteur de fichier du père ne sera par
contre pas fermé (même chose dans le sens inverse).

La synchronisation
● Côté fils: un printf a été fait, cette fois pour afficher "Je suis Le fils". Le pointeur de fichier a donc
avancé de 15 chez le fils, ce qui se répercute chez le père.
● Côté père: le père a exécuté la fonction wait(). Cette fonction permet la synchronisation entre le
père et tous ses fils. Cela signifie que le père va arrêter de s'exécuter (dans ce cas on dit qu'il
dort) jusqu'à ce que son fils se termine complètement.

La fin

Le fils vient d'exécuter sa dernière instruction ; à présent, il n'existe plus. Pendant ce temps-là, le père était
encore en attente, mais il va bientôt se réveiller puisque le fils est terminé. Enfin, le père se terminera lui
aussi.

Création d'un Processus

Règle d'Or : tout processus UNIX peut créer un ou plusieurs processus. Donc pour créer un
processus nouveau, il en faut déjà un existant ... qui va être cloné, puis spécialisé. Il y a donc une filiation
entre les processus !
Primitives

PID Courant : pid_t getpid(void); Retourne le pid du processus courant (appelant)

PID du Père pid_t getppid(void); Retourne le pid du père du processus courant (appelant)

Suspension :
int sleep (int nb_sec); sleep endort le processus jusqu'à ce que nb_sec
secondes se soient écoulées, ou jusqu'à ce qu'un signal non-ignoré soit reçu.

int usleep (int nb_msec);usleep endort le processus jusqu'à ce que nb_msec


micro-secondes se soient écoulées, ou jusqu'à ce qu'un signal non-ignoré soit reçu.

Création de processus : pid_t fork(void); Permet la création dynamique d’un nouveau


processus (fils). par duplication du processus courant (père). Le processus fils ne diffère du processus
père que par les pid et ppid. Les 2 processus (père et fils) s’exécutent de manière concurrente !

Retourne un entier : En cas de succès: 0 dans le fils et pid du fils dans le père
En cas d’échec : -1 dans le père et le fils n’est pas créé
La valeur
retournée par
fork permet donc
de faire la
distinction entre
le père et le fils !

Bibliothèques :

#include
<sys/types.h>

#include
<unistd.h>
Exemple basique
int main(void)
{ pid_t pid;
pid = fork();
if (pid == 0) printf(" Je suis le FILS ");
else printf(" Je suis le PERE ");
return 0; }

Après fork :
● allocation d’une entée dans la table des processus au nouveau processus
● allocation d ’un pid au nouveau processus
● duplication du contexte du processus père (données, pile…)
● retour du pid du processus fils à son père et 0 au processus fils

Synchronisation (les Primitives)

Exit : void exit(int status);


– termine le processus appelant.
– transmet la valeur de status (le mot d’état) au processus père
– ferme les descripteurs de fichiers ouverts
– un signal SIGCHLD est envoyé au processus père.

Wait pid_t wait(int *status);


– attend la terminaison ou l'arrêt d'un processus fils (fils quelconque).
– retourne le PID d'un processus terminé, -1 en cas d’erreur (si tous sont déjà terminés).
– stocke la valeur transmise par le fils (exit) dans l'entier pointé par status.
Exemple complet

Dans le ==père== (PID supposé : 1234) : Dans le ==fils== (PID supposé : 2345):

int main(void) int main(void)


{ int pid, status; { int pid, status;
pid = fork(); pid = fork();
if (pid == 0) { if (pid == 0) {
printf(" Je suis le fils"); printf(" Je suis le fils");
exit(3); exit(3);
} else { } else {
printf(" Je suis le pere "); printf(" Je suis le pere ");
printf(" j ’attends la fin printf(" j ’attends la fin de
de mon fils); mon fils);
wait(&status); wait(&status);
printf(" status = %d printf(" status = %d
",status>>8); ",status>>8);
} }
return 0; return 0;
} }

Affichages :

Je suis le fils
Je suis le pere
j ’attends la fin de mon fils
status = 3

On ne peut pas prédire l'ordre des 3 premiers printf. Par contre on sait forcément que l'affichage du
status se fera en dernier !

● Lorsqu’un processus se termine (exit), le système détruit son contexte, sauf son entrée de la
table des processus
● Le procesus est alors dit dans un état ZOMBI (momentané si bien codé)
● Le processus père récupère la mort de son fils (wait), et détruit son entrée de la table des
processus.
● Le fils disparaît complètement du système (n’est plus zombi).

La communication entre le fils zombi et le père s ’effectue par le biais d ’un signal transmis du fils vers le
père (signal SIGCHLD : la mort du fils). Un processus fils défunt reste zombie jusqu’à ce que son père ait
pris connaissance de sa mort (wait).

Vous aimerez peut-être aussi