Que fait un fork() ?

[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 - Modifié le 30 mai 2022 à 01:38
Note : kilian est l'auteur d'origine de l'astuce.


...ou le petit fork() illustré....


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) afficheront 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 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 retournera 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.

Notes et remerciements

L'image qui précède le sommaire est une modification d'une photographie originale produite par tanakawho.