Département INFormatique 
  CSC4508/M2 : Concepts des systèmes d'exploitation et mise en œuvre sous Unix
    Évaluation

Institut National des Télécommunications
Télécom INT 2è année

TP Noté CS21 du 20/06/05

Corrigés

Modalités

Durée : 3 heures

Les trois questions sont indépendantes les unes des autres.

Le barême est donné à titre indicatif des poids entre les différentes questions. Seront notés :
  • La clarté du code,
  • Le fait que le code compile (sans warning),
  • Le fait que le code répond correctement à la question posée,
  • Le fait que le retour de chaque appel système, s'il y en a un, est testé et géré (traitement d'erreur),
  • Le fait que les ressources système réservées pendant l'exécution du programme sont libérées à la fin de l'exécution.
La «livraison» de votre travail en fin de TP noté se fera par remise de votre copie à l'enseignant et par envoi d'un mail à Michel.Simatic@int-evry.fr (sujet «CS21:TP noté») avec en pièce jointe le fichier d'extension tgz constitué de la manière suivante :
cd votreRepertoireDeTravailPourCS21
tar cvfz $USER.tgz TPNote2005

Préparation

cd votreRepertoireDeTravailPourCS21
cp ~simatic/CS21/tPNote2005.tgz .
tar xvfz tPNote2005.tgz
cd TPNote2005

Question 1 : Relecture de code (4,5 points)

Pour chacun des six extraits de code suivants, déterminez s'il contient des anomalies (erreur de programmation, erreur d'utilisation ou bien code non optimal).
Pour chaque anomalie trouvée, donnez sa localisation et une explication sur votre copie ou bien dans le fichier Q1/reponse.txt (dans ce dernier cas, indiquez sur votre copie "Réponse dans Q1/reponse.txt" ).
/*************/
/* Extrait 1 */
/*************/
Ligne  1: int i;
Ligne  2: pid_t pid;
Ligne  3: /* Creation de NBENFANTS enfants */
Ligne  4: for (i=0 ; i<NBENFANTS ; i++){
Ligne  5:   pid = fork();
Ligne  6:   switch(pid){
Ligne  7:     case -1:
Ligne  8:       perror("fork");
Ligne 10:       exit(EXIT_FAILURE);
Ligne 11:     case 0:
Ligne 12:       i = NBENFANTS;
Ligne 13:       break;
Ligne 14:     default:
Ligne 15:       break;
Ligne 16:   }
Ligne 17: }  







/*************/
/* Extrait 2 */
/*************/
Ligne  1: typedef struct {
Ligne  2:   short nbTorchons;
Ligne  3:   int   nbAssiettes;
Ligne  4:   short nbServiettes;
Ligne  5:   int   nbCuillières;
Ligne  6: } materielCuisine;

/*************/
/* Extrait 3 */
/*************/
Ligne  1: int fd;
Ligne  2: int rc;
Ligne  3: int donnee;
Ligne  4: fd = open("unFichier.dat", O_RDONLY|O_SYNC);
Ligne  5: if (fd == NULL){
Ligne  6:   perror("fopen");
Ligne  7:   exit(EXIT_FAILURE);
Ligne  8: }
Ligne  9: rc = read(fd, &donnee, sizeof(donnee));
Ligne 10: if (rc < 0){
Ligne 11:   perror("read");
Ligne 12:   exit(EXIT_FAILURE);
Ligne 13: }

/*************/
/* Extrait 4 */
/*************/
Ligne  1: void attenteRequete(void *arg);
Ligne  2: int i;
Ligne  3: for (i=0 ; i<NBTHREADS ; i++) {
Ligne  4:   int retcode;
Ligne  5:   pthread_t thread;
Ligne  6:   retcode = pthread_create(&thread, NULL, attenteRequete, (void *)i);
Ligne  7:   if (retcode != 0) {
Ligne  8:     perror("pthread_create");
Ligne  9:     exit(EXIT_FAILURE);
Ligne 10:   }
Ligne 11:   pthread_detach(thread);
Ligne 12: }

/*************/
/* Extrait 5 */
/*************/
Ligne  1: void attenteNaissance(void) {
Ligne  2:   assert(pthread_mutex_lock(&mutexNaissance) >= 0);
Ligne  3:   while (bebeEstNe == FAUX) {
Ligne  4:     assert(pthread_mutex_unlock(&mutexNaissance) >= 0);
Ligne  5:     assert(pthread_cond_wait(&conditionNaissance, &mutexNaissance) >= 0);
Ligne  6:     assert(pthread_mutex_lock(&mutexNaissance) >= 0);
Ligne  7:   }
Ligne  8:   assert(pthread_mutex_lock(&mutexNaissance) >= 0);
Ligne 10: }

/*************/
/* Extrait 6 */
/*************/
Ligne  1: FILE *f;
Ligne  2: char ligne[TAILLEMAX];
Ligne  3: f = popen("programmeExterne", "r");
Ligne  4: if (f == NULL) {
Ligne  5:   perror("popen");
Ligne  6:   exit(EXIT_FAILURE;
Ligne  7: }
Ligne  8: while (feof(f) == 0) {
Ligne  9:   fflush(f);
Ligne 10:   if (fgets(ligne, sizeof(ligne), f) != NULL) {
Ligne 11:     traitement(ligne);
Ligne 12:   }
Ligne 13: }









Question 2 : En voiture (8 points)

Un constructeur automobile vous demande d'écrire un simulateur de sa prochaine usine de construction de voitures. Les composants de cette usine (simplifiée dans la mesure où on n'en simule que la partie "montage des roues") sont :
  • Un  "robot transporteur", simulé à l'aide d'un thread, chargé d'apporter une voiture sur le lieu de montage, d'attendre le montage de ses roues, puis de la retirer avant d'appporter un nouveau véhicule, selon l'algorithme suivant :
robotTransporteur() {
   Faire pour toujours
      apporterVoitureSurLieuMontage();
      signalerVoitureSurLieuMontage();
      attendreMontageRoues();
      retirerVoitureDeLieuMontage();
   Fait;
}
  • Quatre "robots monteur de roues", simulés à l'aide d'un thread par robot, pouvant travailler en parallèle. Chacun de ces robots prend une roue dans un distributeur de roues (partagé entre ces quatre robots), attend qu'un véhicule soit prêt au montage, monte la roue à l'endroit qui lui est assigné (par exemple, avant-gauche), selon l'algorithme suivant :
robotMonteurRoues(Localisation localisationSurVoiture) {
   Faire pour toujours
      Roue roue;
      roue = prendreRoueDansDistributeur();
      attendreVoitureSurLieuMontage(localisationSurVoiture);
      monterRoue(roue, localisationSurVoiture);
      signalerMontageDUneRoueTermine();
   Fait;
}
  • Un "robot chargeur", simulé à l'aide d'un thread, responsable de mettre des roues dans le distributeur selon l'algorithme suivant :
robotChargeur() {
   Faire pour toujours
      mettreRoueDansDistributeur(nouvelleRoue());
   Fait;
}
  • Un "distributeur", simulé à l'aide d'un tableau tabRoue de MAXROUES éléments de type Roue. Le chargement de roues se fait dans l'ordre des cases tabRoue[0], tabRoue[1]... tabRoue[MAXROUES-1], tabRoue[0]... La prise de roue est également réalisée dans l'ordre tabRoue[0], tabRoue[1]... tabRoue[MAXROUES-1], tabRoue[0]...
    Noter que mettreRoueDansDistributeur() attend si aucune case de tabRoue n'est libre et prendreRoueDansDistributeur() attend si aucune case de tabRoue ne contient de roue.
NB : pour la question 2.1 (respectivement 2.2), vous pouvez répondre sur votre copie ou bien dans le fichier Q2/reponse21.txt (respectivement Q2/reponse22.txt). Dans ce dernier cas, indiquez sur votre copie "Réponse dans Q2/reponse21.txt" (respectivement "Réponse dans Q2/reponse22.txt" )

Question 2.1 : Identification des paradigmes (3 points)

Indiquez les différents types de problème de synchronisation auquel vous allez être confrontés pour implémenter ce simulateur (exclusion mutuelle, cohorte, producteur/consommateur...) en précisant, pour chaque problème, ses acteurs (par exemple, si vous trouvez qu'il y a un paradigme de lecteurs/rédacteur dans lequel intervient le "robot chargeur", vous devez préciser si le "robot chargeur" est lecteur ou rédacteur).

Question 2.2 : Conception à l'aide de sémaphores (5 points)

À l'aide de sémaphores (munis de leurs opérations P() et V()) et de variables que vous déclarerez de manière globale (i.e. ils seront accessibles à partir de n'importe quelle procédure/fonction de votre programme) et initialiserez, écrivez les algorithmes suivants :
  • Procédure attendreMontageRoues()
  • Procédure attendreVoitureSurLieuMontage(Localisation localisationSurVoiture)
  • Procédure mettreRoueDansDistributeur(Roue uneRoue)
  • Fonction prendreRoueDansDistributeur() qui renvoie une entité de type Roue
  • Procédure signalerMontageDUneRoueTermine()
  • Procédure signalerVoitureSurLieuMontage()

Question 3 : Toujours plus de "M. et Mme" (7,5 points)

Pour cette question, vous avez à votre disposition :
  • Un serveur de "M. et Mme" constitué d'un processus distributeur et de 26 processus enfants, chacun de ces derniers étant chargé des "M. et Mme" commençant par une certaine lettre de l'alphabet :
    • Chaque enfant cherche, dans le fichier, la première et la dernière ligne commençant par sa lettre.
    • Sur réception d'une requête, le distributeur fait suivre la requête à l'enfant correspondant à la 1-ère lettre du "M. et Mme" :
      • L'enfant ne cherche que dans les lignes qui le concernent ;
      • Il répond au client  ;
      • Il attend une requête du processus distributeur.
    (Certains reconnaîtront à raison le serveur développé à la question 6 de l'exercice 1 des exercices Client/Serveur).
  • Un client capable d'interroger ce serveur.
L'objectif de cette question est de développer une application ajout qui permette d'ajouter dynamiquement des "M. et Mme" au fichier mEtMme.txt en respectant les spécifications suivantes :
  • ajout ne peut ajouter que quand il est sûr qu'aucun enfant du serveur n'est en train de répondre à une requête de "M. et Mme".
  • ajout doit insérer le "M. et Mme" fourni en paramètre au bon endroit dans le fichier (qui est classé alphabétiquement).
  • Quand ajout souhaite insérer un "M. et Mme", il n'y a aucun risque qu'il attende indéfiniment que des enfants du serveur traitent des requêtes de "M. et Mme".
Ceci va entraîner des modifications dans le fichier ajout.c, mais aussi dans le fichier serveur.c. En effet, les processus enfants sont impactés dans la mesure où :
  • Quand ajout insère un nouveau "M. et Mme", les enfants attendent qu'ajout ait fini avant de recommencer à traiter des requêtes de "M. et Mme".
  • Après une insertion d'ajout, avant de recommencer à traiter des requêtes de "M. et Mme", les enfants commencent par réinitialiser la position de la première et de la dernière ligne commençant par la lettre dont ils sont responsables.
Travail demandé :
  1. Indiquez le principe de votre solution sur votre copie ou bien dans le fichier principe.txt (dans ce dernier cas, précisez sur votre copie "Réponse dans Q3/principe.txt).
  2. Modifiez les fichiers ajout.c et serveur.c pour obtenir l'ajout dynamique de "M. et Mme".
NB : Pour cette question, il y a 3 problèmes à résoudre (classés par ordre de difficulté croissante) :
  1. la synchronisation entre ajout et les enfants du serveur,
  2. la mise-à-jour de mEtMme.txt par ajout,
  3. la réinitialisation des enfants
Il est recommandé de les traiter dans cet ordre et de vérifier qu'un problème est traité avant de passer à la résolution du problème suivant (pour information, les 3 problèmes seront notés indépendamment les uns des autres).

Page mise à jour le 15 mai 2006