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é CSC4508/M2 du 07/06/06

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 «CSC4508/M2:TP noté») avec en pièce jointe le fichier d'extension tgz constitué de la manière suivante :
cd votreRepertoireDeTravailPourCS22M1
tar cvfz $USER.tgz TPNote2006

Préparation

cd votreRepertoireDeTravailPourCS22M1
cp ~simatic/CS22M1/tPNote2006.tgz .
tar xvfz tPNote2006.tgz
cd TPNote2006

Question 1 : Questions de cours (5 points)

Pour chacune des questions ci-dessous, répondez sur votre copie ou bien dans le fichier Q1/reponse1.txt (dans ce dernier cas, indiquez sur votre copie "Réponse dans Q1/reponse1.txt" ).

Question 1.1 : À propos des threads

On considère le code suivant :
#include <stdio.h>
#include <stdlib.h>

#include <pthread.h>

#define NBAFFICHAGE 5

void ajoute(char *val)
{
static char cur;

if (val != NULL)
cur = *val;
else
cur = cur + 1;

printf("cur = %c\n", cur);
}


void *pthread_func(void *arg)
{
int i;
char *val = (char *)arg;

ajoute(val);

sleep(2);

for (i = 0; i < NBAFFICHAGE; i++)
ajoute(NULL);

pthread_exit(NULL);
}

int main(int argc, char **argv)
{
pthread_t thread1, thread2;

char val1 = 'a';
char val2 = 'A';

if (pthread_create(&thread1, NULL, pthread_func, &val1))
{
perror("pthread_create");
exit(EXIT_FAILURE);
}

if (pthread_create(&thread2, NULL, pthread_func, &val2))
{
perror("pthread_create");
exit(EXIT_FAILURE);
}

pthread_join(thread1, NULL);
pthread_join(thread2, NULL);

return EXIT_SUCCESS;
}

À un moment donné, combien y-a-t-il au maximum dans la mémoire du processus d'occurrences de:

  • val1
  • val2
  • i
  • cur
  • val

Question 1.2 : Ordonnancement

Un(e) de vos collègues vient de lancer le programme suivant :
/*******************************/
/* Essai de sched_setscheduler */
/*******************************/

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/wait.h>

#define FATAL(msg) {\
char m[128]; \
sprintf(m, "%s in %s:%d:%s", msg, __FILE__, __LINE__, __func__); \
perror(m); \
abort(); \
}

/**********************************************************************/
/* Code execute par le processus enfant */
/**********************************************************************/
void codeEnfant()
{
struct sched_param sp;

sp.sched_priority = sched_get_priority_max(SCHED_FIFO);
if (sched_setscheduler(0,SCHED_FIFO,&sp) < 0) {
FATAL("sched_setscheduler");
}

while(1){
}
}

/**********************************************************************/
/* Programme principal */
/**********************************************************************/
int main(int argc, char* argv[])
{
int status;

switch (fork()) {
case -1:
FATAL("fork");
break;
case 0:
codeEnfant();
break;
default:
wait(&status);
}

return EXIT_SUCCESS;
}
Que conseillez-vous pour arrêter son programme ? Justifiez votre réponse.
Qu'auriez-vous répondu si votre collègue avait utilisé SCHED_RR à la place de SCHED_FIFO ?

Question 1.3 : Mémoire

Le système d'exploitation MS-DOS a été conçu en 1981 de sorte qu'un programme s'exécutant sur ce système ne pouvait accéder qu'à 640 Ko de mémoire vive. Or, à partir de 1988, les ordinateurs sous MS-DOS disposant d'au moins 1 Mo de mémoire vive devenaient communs.
Pour tirer parti de cette mémoire vive supplémentaire, des compilateurs ont alors proposé le mécanisme appelé "mécanisme de recouvrement" (overlay) :
  • Le programmeur pouvait spécifier que des sections de son code devaient être rangées dans des fichiers appelés "fichiers de recouvrement" (ou fichiers d'overlay).
  • Ainsi un programme exécutable était constitué par :
    • Un fichier exécutable contenant le main du programme.
    • Un ou plusieurs fichiers de recouvrement, chargés dans la mémoire au delà des 640 Ko au moment de l'exécution.
  • La zone de 640 Ko contenait une zone appelée "zone de recouvrement". Pour être utilisé, un fichier de recouvrement devait être transféré dans cette zone de recouvrement. Le programmeur souhaitant appeler une fonction située dans un fichier de recouvrement devait au préalable invoquer une primitive permettant le transfert du fichier de recouvrement dans la zone de recouvrement. L'appel de la fonction pouvait ensuite se faire normalement.
    NB : si le programmeur avait ensuite besoin d'invoquer une fonction présente dans un autre fichier de recouvrement, il devait préalablement demander le transfert de cet autre fichier qui recouvrait (remplaçait) alors le premier fichier dans la zone de recouvrement.
  • Voici un exemple de main d'un programme qui utilisait 3 fonctions a et b stockées dans le fichier fichierRecouvrementAB et 2 fonctions y et z stockées dans le fichier fichierRecouvrementYZ :
    int main(){
    chargerFichierRecouvrement(fichierRecouvrementAB);
    a(); /* NB : a() appelle b(), ce qui ne pose aucun problème   */
    /* puisque b() est présente dans la zone de recouvrement */
    chargerFichierRecouvrement(fichierRecouvrementYZ); /* NB : ce fichier recouvre le fichier */
    /* fichierRecouvrementAB : a() et b() */
    /* ne sont donc plus accessibles */
    y();
    z();
    chargerFichierRecouvrement(fichierRecouvrementAB);
    a();
    return EXIT_SUCCESS;
    }
Présentez au moins un avantage et au moins deux inconvénients du mécanisme de recouvrement par rapport à un mécanisme de mémoire virtuelle.

Question 2 : Il était une fois... (7 points)

Il était une fois une jeune et jolie jeune fille qui s'appelait Blanche-Neige. Tous les matins, les chauds rayons du soleil venait la tirer de son doux et beau sommeil. Pleine d'énergie, elle réveillait les sept nains (avec qui elle vivait) afin qu'ils ne soient pas en retard pour leur travail à la mine.
Une fois habillés, puis arrivés devant la mine, les sept nains devaient éventuellement attendre que les trois ours (qui travaillaient parfois à la mine pendant que les nains n'y étaient pas) en soient sortis (les ours et les nains n'arrivaient pas à cohabiter dans la mine : les ours estimaient que la barbe des nains grattait ; quant aux nains, ils trouvaient que les ours ne savaient pas siffler en travaillant). Une fois les ours sortis (pour rejoindre la tendre Bouton d'Or, mais cela est une autre histoire), les nains entraient dans la mine pour commencer leur travail qu'ils entrecoupaient de pauses régulières au cours desquelles ils se réconfortaient à l'aide d'un verre d'alcool du pays préparé par la Grand-mère du Petit Chaperon rouge. Ils trouvaient ces verres sur un plateau (situé à l'entrée de la mine), les buvaient (cul sec !) et les jetaient par dessus leur épaule (ces nains étaient d'origine russe). Ce plateau (qui pouvait contenir au plus quatre verres pleins) était rempli par 8 adorables petites mésanges qui faisaient constamment l'aller-retour Maison de Mère-Grand/Mine avec un verre rempli (ces mésanges attendant qu'une place soit libérée sur le plateau si jamais le plateau contenait quatre verres pleins).
Le soir venu, les nains quittaient la mine (permettant ainsi aux ours de rentrer dans la mine s'ils avaient décidé de venir y travailler) et rentraient à la maison. Là, sans se préoccuper que le soleil soit couché ou non, Blanche-Neige leur faisait "Bisou Bonne Nuit" pour qu'ils s'endorment. Elle allait ensuite se coucher en attendant une nouvelle journée pleine d'aussi merveilleuses aventures...

Cette histoire, vous l'avez raconté des centaines de fois à l'enfant dont vous êtes le ou la baby-sitter et... vous craquez ! Aussi, vous avez décidé de réfléchir à un algorithme qui servirait de base à un programme racontant automatiquement cette histoire.

Vous envisagez les tâches suivantes :
  • Une tâche Soleil racontant l'histoire du point de vue du soleil. Elle exécute l'algorithme suivant :
    faire
       Ecrire "Je me lève"
       dormir(12 heures)
       Ecrire "Je me couche"
       dormir(12 heures)
    tant que VRAI

  • Une tâche BlancheNeige racontant l'histoire du point de vue de Blanche-Neige. Elle exécute l'algorithme suivant :
    faire
       Ecrire "Le soleil me reveille"
       Ecrire "Je réveille les nains"
       Ecrire "Je vis ma vie sans les nains"
       Ecrire "Je fais Bisou Bonne Nuit à chaque nain"
       Ecrire "Je dors"
    tant que VRAI

  • 7 tâches Nain (une pour chacun des nains) racontant l'histoire du point de vue de chaque nain. Elles exécutent l'algorithme suivant :
    faire
       Ecrire "Blanche-Neige me reveille"
       Ecrire "Je vais à la mine"
       Ecrire "J'attends de pouvoir entrer dans la mine"
       Ecrire "J'entre dans la mine"
       faire
          Ecrire "Je travaille"
          dormir(nombreDeMinutesAleatoire)
          Ecrire "Je prends ma pause"
          Ecrire "Je prends un verre plein"
          Ecrire "Je bois cul sec !"
       tant que continuerTravail()
       Ecrire "Je quitte la mine"
       Ecrire "Je rentre à la maison"
       Ecrire "Blanche-Neige me fait Bisou Bonne Nuit"
       Ecrire "Je m'endors"
     tant que VRAI

  • 3 tâches Ours (une pour pour chacun des ours) racontant l'histoire du point de vue de chaque ours. Elles exécutent l'algorithme suivant :
     faire
        Ecrire "Je vis une autre histoire avec Bouton d'Or"
        dormir(nombreDeMinutesAleatoire)
        si oursDecideDAllerTravaillerALaMine() == VRAI alors
          
    Ecrire "Je vais à la mine"
           Ecrire "J'attends de pouvoir entrer dans la mine"
           Ecrire "J'entre dans la mine et je travaille"
           dormir(nombreDeMinutesAleatoire)
           Ecrire "Je quitte la mine"
        finsi

     tant que VRAI

  • 8 tâches Mesange (une pour chacune des mésanges) racontant l'histoire du point de vue de chaque mésange. Elles exécutent l'algorithme suivant :
     faire
        Ecrire "Je prends un verre plein chez Mère-Grand, je vais à la mine et j'attends de pouvoir le déposer sur le plateau"
        Ecrire "Je dépose le verre sur le plateau"
     tant que VRAI
NB : pour les questions 2.1 et 2.2, vous pouvez répondre sur votre copie ou bien dans le fichier Q2/reponse2.txt. Dans ce dernier cas, indiquez sur votre copie "Réponses dans Q2/reponse2.txt".

Question 2.1 : Identification des paradigmes (2 points)

Indiquez les différents types de problème de synchronisation auxquels vous allez être confrontés pour implémenter ce programme (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 "soleil", vous devez préciser si le "soleil" est lecteur ou rédacteur).
Pour chacune des questions ci-dessous, répondez sur votre copie ou bien dans le fichier Q2/reponse.txt (dans ce dernier cas, indiquez sur votre copie "Réponse dans Q2/reponse.txt" ).

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

À l'aide de sémaphores IPC (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, complétez les algorithmes envisagés pour que le programme raconte correctement l'histoire.

Question 3 : Nains, mésanges et code (8 points)

Vous décidez d'implémenter une version simplifiée des algorithmes de la question 2. Les tâches suivantes sont envisagées :
  • 7 tâches Nain (une pour chacun des nains) racontant l'histoire (simplifiée) du point de vue de chaque nain. Elles exécutent l'algorithme suivant :
    faire
       Ecrire "Nain <numéroNain> : Je travaille"
       dormir(1 seconde)
       Ecrire "Nain <numéroNain> : Je prends ma pause"
       Ecrire "Nain <numéroNain> : Je prends un verre plein sur le plateau"
       Ecrire "Nain <numéroNain> : Je bois cul sec !"
     tant que VRAI

  • 8 tâches Mesange (une pour chacune des mésanges) racontant l'histoire (simplifiée) du point de vue de chaque mésange. Elles exécutent l'algorithme suivant :
     faire
        Ecrire "Mésange <numéroMésange> : Je prends un verre plein chez Mère-Grand, je vais à la mine et j'attends de pouvoir le déposer sur le plateau"
        Ecrire "Mésange <numéroMésange> : Je dépose le verre sur le plateau"
     tant que VRAI

Question 3.1 : Conception (2 points)

Décrire dans Q3/reponse31.txt les algorithmes envisagés dans le cas de l'utilisation de threads, avec des mutex et des conditions (le plateau sera représenté par un tableau d'entier de 4 cases : une case à 0 signifie que cette partie du plateau est vide tandis qu'une case à 1 signifie que cette partie du plateau contient un verre plein.
NB : Si vous ne voyez pas comment écrire ces algorithmes (et que vous souhaitez, malgré tout, répondre à la question 3.2), vous avez la possibilité de faire "appel à un cabinet de consulting" en demandant une indication à l'enseignant qui notera qu'il vous a aidé : donc, le(s) point(s) de barême associé(s) à cet(/ces) algorithmes ne vous sera(/seront) pas comptabilisé(s).

Question 3.2 : Implémentation avec des threads (6 points)

Implémenter (avec des threads, des conditions et des mutex) la conception envisagée à la question 3.1 en modifiant le fichier Q3/conteDeFeeOuCauchemar.c
NB : <numéroNain> sera remplacé par un numéro entre 1 et 7 tandis que <numéroMésange> sera remplacé par un numéro entre 1 et 8



Page mise à jour le 7 juin 2006