CSC 3102 – Introduction aux systèmes d’exploitation

Portail informatique

TP6.2 – Les tubes

Pour faire les exercices, vous avez besoin de connaître le langage bash. Vous pouvez vous référer à l'annexe shell. Vous pouvez aussi trouver une liste d'astuces ici. Tous les exercices sont obligatoires, sauf les exercices notés « défi » ou « optionnel » qui sont optionnels. En particulier, les exercices notés « hors présentiel » sont supposés fait d'une séance sur la suivante.

  • Manipuler les tubes (mkfifo)
  • Manipuler les signaux (kill, trap)
  • Réviser les redirections

La boîte à Meuh (∼2h)

Le but de cet exercice est de transformer le Colisée que vous avez créé précédemment (voir ici) en boîte à Meuh, c'est-à-dire que les processus vont afficher Meuh les uns après les autres.

Même si afficher Meuh sur le terminal peut sembler quelque peu inutile, l'algorithme que vous allez mettre en œuvre est souvent utilisé dans les systèmes répartis et réseaux pour synchroniser un ensemble de processus. Par exemple, l'algorithme qu'on vous demande de mettre en œuvre est utilisé pour construire le protocole Token-Ring qui permet un accès équitable à une infrastructure réseau.
pas d'image, désolé !

Techniquement, les processus sont organisés en anneau, comme présenté dans la figure ci-dessus. Chaque processus de la chaîne crée un tube nommé tube-X-N, où X est le PID du processus initial (stocké dans la variable pidInitial) et N l'argument donné au script, c.-à-d., le nombre de processus restant à créer dans la chaîne. Pendant l'exécution, les processus communiquent soit avec leur prédécesseur en accédant au tube du prédécesseur, soit avec leur successeur, en accédant à leur propre tube.

À partir de cette topologie, les processus échangent un jeton, symbolisé par le mot Meuh. Un processus reçoit le jeton lorsqu'il reçoit le mot Meuh via le tube de son prédécesseur. Le processus conserve ensuite le jeton pendant une seconde avant de le passer à son successeur en écrivant Meuh dans son tube.

Pour commencer, il faut que chaque processus trouve (i) le nom du tube pour communiquer avec son prédécesseur et (ii) le nom du tube pour communiquer avec son successeur.
  • Le nom du tube successeur est toujours tube-$pidInitial-N, où pidInitial est le PID du processus initial et N l'argument du script (i.e., $1).
  • Pour le nom du tube prédécesseur :
    • si le processus est le processus initial (c.-à-d., le seul processus démarrant avec une variable pidInitial non initialisée), ce nom est tube-$$-1, où $$ est le PID du processus courant, puisque le processus courant est le processus initial,
    • sinon, le nom du tube est tube-$pidInitial-M, où M vaut $1+1,

Après avoir copié gladiateur.sh en meuh.sh, modifiez votre script pour :
  • stocker dans les variables pred et succ les noms des tubes du prédécesseur et du successeur,
  • modifier l'affichage Processus $$ démarre avec le processus initial $pidInitial de façon à afficher les noms de ces tubes.
On ne vous demande pas de créer les tubes à cette question.
$ ./meuh.sh 3 Processus 3158 démarre avec le processus initial 3158 (tube-3158-1 - tube-3158-3) Il reste 2 processus à créer 3158: Ave César Processus 3160 démarre avec le processus initial 3158 (tube-3158-3 - tube-3158-2) Il reste 1 processus à créer 3160: Ave César Processus 3164 démarre avec le processus initial 3158 (tube-3158-2 - tube-3158-1) Fin de chaîne 3164: Ave César 3158: Ave César 3160: Ave César 3164: Ave César ^C3164: Morituri te salutant 3160: Morituri te salutant 3158: Morituri te salutant

Modifiez votre script de façon à ce que chaque processus de la chaîne crée son tube successeur après avoir identifié les noms des tubes successeurs et prédécesseurs.

Avant d'aller plus loin, il faut être capable de supprimer les tubes créés par notre application. Modifiez cesar.sh de façon à supprimer chacun des tubes créés. Vous pouvez remarquer que, comme meuh.sh exporte pidInitial, cette variable est positionnée dans cesar.sh. Pour cette raison, il suffit de supprimer tous les fichiers dont le nom commence par tube-$pidInitial- dans cesar.sh. Pensez à utiliser l'option -f de la commande rm, qui évite de demander une confirmation à l'utilisateur.

Pour accéder aux tubes, nous utilisons des redirections avancées. En effet, des redirections simples ouvrent et ferment continuellement les tubes, ce qui entraîne des erreurs ou des blocages lorsque les tubes sont fermés pendant qu'il existe des interlocuteurs. Avant la boucle principale qui affiche Ave César, ouvrez en lecture/écriture les tubes prédécesseurs et successeurs (2 ouvertures différentes). Vous devriez remarquer que vous avez un message d'erreur du type : « mkfifo: tube-3665-1: File exists ». Nous nous occuperons de ce message à la question suivante.

Le message d'erreur que vous avez est dû à un problème de synchronisation. Le processus initial arrive à atteindre l'ouverture du tube du prédécesseur avant que le processus final ait eu le temps créer son tube. Comme l'ouverture par le processus initial crée un fichier normal, la création du tube dans le processus final échoue avec un message d'erreur. Pour éviter ce problème, il ne faut donc ouvrir le tube du prédécesseur que si le tube existe. Avant d'ouvrir le tube du prédécesseur, ajoutez donc une boucle qui attend tant que le tube n'existe pas (donné par [ ! -e $pred ]) et qui dort une seconde à chaque pas de boucle. Vérifiez que le message d'erreur a bien disparu.

De façon à mieux voir comment fonctionne le protocole, nous lançons chaque processus dans un terminal différent. Remplacez la création récursive d'un processus avec meuh.sh K, où K est égal au nombre de processus restant à créer dans la chaîne, par xterm -reverse -e meuh.sh K (l'option reverse crée un terminal blanc sur fond noir et est optionnelle, alors que l'option -e indique à xterm le nom du programme à exécuter dans le terminal). Vous pouvez admirer votre protocole de terminaison qui permet de fermer tous les terminaux en saisissant control-c dans n'importe quel terminal.

Nous mettons maintenant en place notre boîte à Meuh. Dans la boucle principale, remplacez l'affichage de Ave César et l'attente de cinq secondes par :
  • une lecture d'une ligne à partir du tube prédécesseur,
  • l'affichage de la ligne lue,
  • une attente d'une seconde,
  • l'écriture de la ligne lue dans le tube successeur.
Pour amorcer votre boîte à Meuh, écrivez « Meuh » dans un des tubes à partir d'un autre terminal.

Amorcer la boîte à Meuh à partir d'un autre terminal est relativement fastidieux. Pour cette raison, c'est le processus final qui va générer le premier jeton. Modifiez votre script pour que le processus final (celui qui a pour paramètre 1) écrive Meuh dans le tube de son successeur juste avant d'exécuter la boucle principale du programme.
Félicitation, vous venez d'écrire votre premier protocole multiprocessus complexe !