TP noté I52 2009

par Joseph Razik, le 2009-12-12

Exercice 1 (7 points)

On se propose de simuler le jeu du nombre mystère dans lequel plusieurs joueurs, représentés par des processus fils, doivent trouver un nombre mystère, en envoyant au serveur meneur de jeu (le processus père) le nombre que chaque joueur propose, jusqu’à ce qu’un des joueurs ait trouvé la bonne réponse. Pour plus de simplicité, le nombre mystère sera passé en paramètre du programme, et sera un entier compris entre 0 et 20.

On se propose de résoudre ce problème de manière progressive.

Question 1:

Ecrire le programme ``exam1v1.c`` qui prend en paramètre le nombre n de joueurs, ainsi qu’un entier entre 0 et 20 (le nombre mystère à trouver) et qui lance en parallèle les n joueurs.

Chaque joueur s’endort pendant 1 à 3 secondes, puis tire un nombre aléatoire, compris entre 0 et 20, et l’affiche à l’écran avec le message “Je suis le joueur numero X et j’ai tiré au hasard le nombre Y” où X désigne le numéro du joueur (entre 1 et n) et Y désigne l’entier aléatoire tiré par ce joueur.

Le serveur, lui, affiche le nombre mystère à trouver, puis attend la fin de tous ses fils avant de se terminer.

Question 2:

Recopiez votre programme précédent exam1v1.c dans un autre fichier de nom ``exam1v2.c``

Maintenant, on va compléter le programme précédent afin que chaque joueur (chaque fils) communique au serveur (le père) l’entier qu’il propose, par le moyen d’un tube (anonyme). Pour l’instant, chaque joueur n’aura droit qu’à 8 essais maximum.

Modifier votre programme précédent (dans le fichier exam1v2.c), de telle sorte que le serveur et les joueurs aient les comportements suivants:

Comportement du serveur: (après le lancement des n joueurs)

  • Tant qu’il y a quelque chose à lire dans le tube, effectue le traitement suivant :
    • lit dans le tube le nombre envoyé par un joueur, puis affiche à l’écran le message: | “Serveur : j’ai reçu le nombre X” où X est le nombre envoyé par le joueur
    • Compare le nombre reçu au nombre mystère
    • Si les nombres sont identiques, affiche le message : | “Serveur : le nombre mystere a ete trouve en Y coups” où Y est le nombre de coups total reçu par le serveur

Remarque: on rappelle que la fonction read retourne 0 si le tube est vide et qu’il n’y a plus d’écrivains.

Comportement d’un joueur:

  • S’endort entre 1 et 3 secondes avant de commencer
  • Effectue le traitement suivant tant que 8 essais n’ont pas été effectués:
    • Tire un nombre aléatoire entre 0 et 20
    • Affiche le message suivant: “Joueur no i : j’envoie au serveur le nombre X (c’est mon essai no Y)
    • Envoie ce nombre par le tube au serveur
    • S’endort 2 secondes

Remarques : Pour initialiser le générateur de nombres aléatoires dans chaque joueur, vous utiliserez l’instruction : srand(time(NULL)*getpid());

Question 3:

Recopiez votre programme précédent exam1v2.c dans un autre fichier de nom ``exam1v3.c``

On va améliorer le programme précédent (qui n’est pas satisfaisant) en forçant la terminaison de tous les joueurs dès que le nombre mystère a été trouvé. Pour cela, le serveur enverra le signal SIGUSR1 à tous les joueurs dès qu’un joueur a trouvé la bonne réponse. A la réception du signal SIGUSR1, chaque joueur affichera le message “Signal SIGSUR1 reçu. Fin du jeu, je m’arrête”, et s’arrêtera. Le nombre d’essais autorisé n’est plus limité pour chaque joueur.

Pour envoyer un signal à tous les processus fils, vous pouvez :

  • soit stocker dans un tableau les PID des fils (au moment de leur génération) afin de réutiliser chacun de ces PID au moment de l’envoi des signaux SIGUSR1;
  • soit envoyer le signal à tout le groupe du processus père (en mettant 0 comme valeur de pid dans la fonction d’envoi de message);

Modifier votre programme précédent (dans le fichier exam1v3.c) pour que le serveur et les clients aient les comportements décrits ci-dessus.

Voici un exemple d’exécution avec 2 joueurs et 15 comme nombre mystère à trouver:

./exam1v3 2 15
Je suis le serveur de pid 14584, le nombre mystere a trouver est : 15
Je suis le serveur, je lance le joueur no 1
Je suis le serveur, je lance le joueur no 2
Joueur no 1 : j'attends 2 sec. avant de commencer
Joueur no 2 : j'attends 1 sec. avant de commencer
Joueur numero 2 : j'envoie au serveur le nombre 18 (c'est mon essai 1)
Serveur : j'ai recu le nombre 18
Joueur numero 1 : j'envoie au serveur le nombre 9 (c'est mon essai 1)
Serveur : j'ai recu le nombre 9
Joueur numero 2 : j'envoie au serveur le nombre 4 (c'est mon essai 2)
Serveur : j'ai recu le nombre 4
Joueur numero 1 : j'envoie au serveur le nombre 16 (c'est mon essai 2)
Serveur : j'ai recu le nombre 16
Joueur numero 2 : j'envoie au serveur le nombre 7 (c'est mon essai 3)
Serveur : j'ai recu le nombre 7
Joueur numero 1 : j'envoie au serveur le nombre 20 (c'est mon essai 3)
Serveur : j'ai recu le nombre 20
Joueur numero 2 : j'envoie au serveur le nombre 5 (c'est mon essai 4)
Serveur : j'ai recu le nombre 5
Joueur numero 1 : j'envoie au serveur le nombre 18 (c'est mon essai 4)
Serveur : j'ai recu le nombre 18
Joueur numero 2 : j'envoie au serveur le nombre 19 (c'est mon essai 5)
Serveur : j'ai recu le nombre 19
Joueur numero 1 : j'envoie au serveur le nombre 20 (c'est mon essai 5)
Serveur : j'ai recu le nombre 20
Joueur numero 2 : j'envoie au serveur le nombre 15 (c'est mon essai 6)
Serveur : j'ai recu le nombre 15
Serveur : le nombre mystere a ete trouve en 11 coups
Signal SIGUSR1 recu du serveur, donc je m'arrete
Signal SIGUSR1 recu du serveur, donc je m'arrete



Exercice 2 (7 points)

L’objectif de l’exercice consiste en l’échange d’un message à travers une ressource critique du style tampon d’une seule unité mémoire. L’échange s’effectue entre un processus producteur et un processus consommateur, le tout arbitrer pour la synchronisation par un processus serveur intermédiaire.

Les différentes questions vont permettre d’avancer graduellement vers cet objectif en utilisant les objets IPC de type sémaphore et zone de mémoire partagée (en bonus).

Rappel: Quelques commandes shell concernant les IPC:

  • ipcs: lister les ressources IPC créées et actives du système
  • ipcrm: effacer une ressources IPC

Question 1

Soit un processus p1, appelé aussi serveur. Ecrire dans exam2p1q1.c le code de ce processus qui réalise les étapes suivantes:

  • créer un sémaphore à une valeur,
  • initialiser le sémaphore à 0,
  • s’endormir 5 secondes
  • détruire le sémaphore et se terminer.

Attention, la clé de ce sémaphore ne sera pas privée mais initialisée avec ftok. Le deuxième paramètre pourra (clé de ressource) par exemple être A.

Ce processus affichera:

  • serveur: la cle du semaphore A est xxx et son id est yyy” où xxx représente le résultat de ftok et yyy l’identificateur du sémaphore;
  • serveur: semaphore A initilise a z” une fois le sémaphore initialisé à la valeur z;
  • serveur: destruction des IPCs” au moment de la destruction.

Question 2

Recopiez votre programme précédent exam2p1q1.c dans un autre fichier de nom ``exam2p1q2.c``

Modifier ce code de manière à ce que le processus s’endorme indéfiniment et que l’arrêt de ce processus par la combinaison de touches Ctrl+C détruise le sémaphore avant de quitter.

Rappel: la combinaison de touches Ctrl+C envoie le signal SIGINT au processus et le traitement par défaut de ce signal est la terminaison du processus.

Question 3

Recopiez votre programme précédent exam2p1q2.c dans un autre fichier de nom ``exam2p1q3.c``

Soient deux autres processus indépendants p2 et p3, l’un de type producteur d’une ressource, l’autre de type consommateur de cette ressource. Pour modéliser la production et la consommation de la ressource, il est proposé d’utiliser un sémaphore partagé entre les deux processus. Quand une ressource est produite, la valeur du sémaphore est incrémentée. Quand une ressource est consommée, la valeur du sémaphore est décrémentée.

Le sémaphore utilisé est celui créé par le processus p1 de la question précédente. Afin de pouvoir effectuer leur traitement, les processus p2 et p3 doivent s’assurer que le sémaphore a déjà été créé, c’est-à-dire en pratique que le processus serveur p1 est lancé avant les autres, par exemple dans un autre terminal. Dans le cas contraire un message d’erreur sera affiché et le programme terminé (“producteur/consommateur: erreur, semaphore non cree”).

Les producteur/consommateur afficheront respectivement les messages suivants:

  • producteur: ressource produite
  • consommateur: ressource consommee

De plus, le processus p1 affichera régulièrement (toutes les 2 secondes) la valeur du sémaphore. Ecrire les programmes exam2p1q3.c, exam2p2q3.c et exam2p3q3.c de ces trois processus p1, p2 et p3 respectant les contraintes énoncées.

Lancer plusieurs fois les processus p2 et/ou p3 afin de vous rendre compte de lévolution de la production et de la consommation de la ressources.

Question 4

Recopiez vos programmes précédents exam2p1q3.c, exam2p2q3.c et exam2p3q3.c dans d’autres fichiers de nom respectivement ``exam2p1q4.c``, ``exam2p2q4.c`` et ``exam2p3q4.c``

La ressource utilisée par les deux processus p2 et p3 est en fait unique et critique: le tampon d’échange de la ressource entre les deux processus ne peut contenir qu’un seul objet.

Le sémaphore du serveur prend alors la signification de tampon non vide. Modifier le code du processus serveur p1 précédent afin:

  • de modifier la signification du sémaphore de clé de ressource A soit tampon non vide;
  • d’ajouter un nouveau sémaphore (par exemple de clé de ressource B) dont la signification sera tampon non plein.

Modifier le code des processus p2 et p3 précédents afin de tenir compte de ce nouveau sémaphore et de sa signification. Penser à ajouter les affichages nécessaires.

Question 5 facultative - Bonus

Recopiez vos programmes précédents exam2p1q4.c, exam2p2q4.c et exam2p3q4.c dans d’autres fichiers de nom respectivement ``exam2p1q5.c``, ``exam2p2q5.c`` et ``exam2p3q5.c``

On décide d’utiliser pour le tampon de communication entre les processus p2 et p3 une zone de mémoire partagée (SHM) qui pourra contenir une chaîne de caractère d’au plus 256 caractères. Cette chaîne constitue le message mais aussi la ressource que s’échange les deux processus.

Modifier les codes des processus précédents (p1, p2, p3) en conséquence sachant que:

  • la création/destruction de la zone est gérée par p1,
  • le message est donné en paramètre du processus p2,
  • p3 affiche le message une fois celui-ci reçu.