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


    Évaluation



TELECOM & Management SudParis
TELECOM SudParis 2ème année

TP Noté CSC4508/M2 du 16/05/08

(Corrigés)

Modalités

Durée : 3 heures

Tous documents autorisés.

Les questions 1, 2, 3 et 4 sont indépendantes les unes des autres. Aussi, n'hésitez pas à lire tout le sujet avant de commencer pour déterminer l'ordre dans lequel vous souhaitez traiter les questions.

Le barème est donné à titre indicatif des poids entre les différentes questions.

La « livraison » de votre travail en fin de TP noté se fera par remise de votre copie à l'enseignant et par remontée sous Moodle (rubrique « TP noté de 3 heures ») du fichier d'extension tgz constitué de la manière suivante :
cd votreRepertoireDeTravailPourCSC4508M2
tar cvfz $USER.tgz TPNote2008

Préparation

cd votreRepertoireDeTravailPourCSC4508M2
cp ~simatic/Cours/CSC4508/tPNote2008.tgz .
tar xvfz tPNote2008.tgz
cd TPNote2008

Question 1 : Bugs non reproductibles ? (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 : Premier programme (3 points)

Expliquez brièvement ce que fait le programme correspondant au fichier  Q1/prog1.c
/***********/
/* prog1.c */
/***********/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#define NOMFIC "/tmp/ficPourProg1"
#define NBENREG 1000
#define ENREGFORMATE "Enregistrement #1234567\n"

int main() {
  int i;
  FILE *ficW;
  int ficR;
  char buf[256];

  ficW = fopen(NOMFIC, "w");
  for (i=0 ; i<NBENREG; i++){
    fprintf(ficW, "Enregistrement #%7d\n", i);
  }
  fclose(ficW);

  ficR = open(NOMFIC, O_RDONLY);
  for (i=0 ; i<NBENREG; i++){
    read(ficR,
     (void*)buf,
     strlen(ENREGFORMATE));
    buf[strlen(ENREGFORMATE)-1] = '\0';
    printf("Chaine lue : \"%s\"\n", buf);
  }
  close(ficR);

  return EXIT_SUCCESS;
}

---beginCorr
barème : 0,5 point
Ce programme crée un fichier /tmp/ficPourProg1 dans lequel il écrit 1000 enregistrements sous la forme « Enregistrement #    985 ». Il ferme ensuite ce fichier avant de le réouvrir en lecture afin de lire ces différents enregistrements.
---endCorr

Ce programme fonctionne parfaitement, sauf sur la machine ayant pour hostname « assous » où il se met à répéter une certaine ligne (la ligne répétée changeant à chaque nouvelle exécution du programme). Par exemple, lors d'une exécution, on peut avoir l'affichage :
Chaine lue : "Enregistrement #    983"
Chaine lue : "Enregistrement #    984"
Chaine lue : "Enregistrement #    985"
Chaine lue : "Enregistrement #    985"
Chaine lue : "Enregistrement #    985"
Chaine lue : "Enregistrement #    985"
Chaine lue : "Enregistrement #    985"
cette ligne « Chaine lue : "Enregistrement #    985" » étant répétée 15 fois au total.

Modifiez le fichier Q1/prog1.c pour que toutes les éventuelles erreurs à l'exécution puissent être détectées (en cas d'erreur détectée, on arrêtera immédiatement l'exécution du programme).
---beginCorr
barème : 0,25 point par vérification d'erreurs correcte, soit 1,5 point au total.
prog1.corrige.c
---endCorr

Cette nouvelle version du programme est exécutée sur la machine « assous ». On obtient le message d'erreur suivant :
Erreur 'No space left on device' lors ecriture fichier /tmp/ficPourProg1
Expliquez cette erreur, puis décrivez une expérience permettant de reproduire sur n'importe quelle machine le problème observé sur la machine « assous ». NB : on ne demande pas de réaliser cette expérience.
---beginCorr
barème :
  • explication de l'erreur : 0,5 point ;
  • expérience : 0,5 point ;
Le problème vient du fait que la partition disque de la machine « assous » dans laquelle est écrit le fichier /tmp/ficPourProg1 est saturée. Donc, pour reproduire le problème sur n'importe quel machine, il suffit de saturer la partition disque hébergeant le répertoire /tmp avant de lancer le programme.
---endCorr

Question 1-2 : Deuxième programme (2 points)

Expliquez brièvement ce que fait le programme correspondant au fichier Q1/prog2.c

/***********/
/* prog2.c */
/***********/

#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

#define SHMKEY 30
#define TAILLE 1<<26

int main() {
  char *shm_adr;
  int shm_id;
  shm_id = shmget(SHMKEY, TAILLE, 0700|IPC_CREAT);
  shm_adr = shmat(shm_id, 0, 0);
  memset(shm_adr, 0, TAILLE);
  return EXIT_SUCCESS;
}

Ce programme fonctionne parfaitement, sauf sur la machine ayant pour hostname « oiserie » où il s'arrête avec le message suivant :
Erreur de segmentation (core dumped)

Modifiez le fichier Q1/prog2.c pour que toutes les éventuelles erreurs à l'exécution puissent être détectées (en cas d'erreur détectée, on arrêtera immédiatement l'exécution du programme).

Cette nouvelle version du programme est exécutée sur la machine « oiserie ». On obtient le message d'erreur suivant :
shmget: Invalid argument
Expliquez cette erreur (barème : 0,5 point), puis décrivez une expérience permettant de reproduire sur n'importe quelle machine le problème observé sur la machine « oiserie » (barème : 0,5 point). NB : on ne demande pas de réaliser cette expérience.
---beginCorr
Ce programme crée une zone de mémoire partagée de 64 Mo dont il initialise tous les octets à zéro.
Voici le programme corrigé : prog2.corrige.c
Pour reproduire cette erreur sur n'importe quelle machine, il faut saturer la mémoire du système avant de lancer ce programme.
barème :
  • objectif du programme : 0,5 point ;
  • ajout du code de correction : 0,25 point par contrôle, soit 0,5 point au total ;
  • explication de l'erreur : 0,5 point ;
  • proposition d'expérience : 0,5 point ;
---endCorr

Question 2 : Rendez-vous conditionnel (5 points)

Le commentaire du transparent 3.3.2 (rendez-vous entre deux processus) du cours « Synchronisation entre processus » présente un algorithme pour un rendez-vous entre N processus à l'aide de sémaphores P et V.
Écrire, à l'aide des opérations pthread_mutex_lock et pthread_mutex_unlock d'objets mutex thread et des opérations pthread_cond_wait et pthread_cond_broadcast  (NB : on n'utilisera pas l'opération pthread_cond_signal) d'objets condition, l'algorithme d'une procédure rdv permettant de programmer un rendez-vous entre N threads.
Rappelons que, quand un thread appelle cette procédure rdv :
  • s'il est le 1er, le 2ème... ou le N-1ème à l'appeler, ce thread se retrouve bloqué, en attente, dans cette procédure ;
  • s'il est le Nème à l'appeler, ce thread débloque les N-1 autres threads en attente et poursuit son exécution.
NB : Pour simplifier l'algorithme de rdv, on suppose que chaque thread n'appelle rdv qu'une seule fois.

Pour cette question, répondez sur votre copie ou bien dans le fichier Q2/reponse2.txt (dans ce dernier cas, indiquez sur votre copie « Réponse dans Q2/reponse2.txt »).
---beginCorr
barème :
  • toutes les variables sont là : 1 (- 0,33 par variable manquante) ;
  • la variable nbArrivesAuRDV est correctement initialisée : 1 ;
  • 3 points pour l'algorithme
Variables globales
==================
// Nombre de processus arrivés au RDV
int nbArrivesAuRDV = 0

// Mutex et condition
pthread_mutex_t mutex
pthread_cond_t condToutLeMondeEstLa

Algorithme de rdv
=================
pthread_mutex_lock(&mutex)
nbArrivesAuRDV = nbArrivesAuRDV + 1
si nbArrivesAuRDV < N alors
repeter
pthread_cond_wait(&condToutLeMondeEstLa, &mutex)
jusqu'à nbArrivesAuRDV == N
sinon
pthread_cond_broadcast(toutLeMondeEstLa)
finsi
pthread_mutex_unlock(&mutex)
NB : supposons qu'un thread appelle 2 fois consécutivement rdv. Dans ce cas, le dernier thread arrivé dans le premier appel à rdv (donc, celui qui réveille tous les autres) peut immédiatement après appeler à nouveau rdv avant que les autres n'aient effectivement été réveillés. La valeur de nbArrivesAuRDV ne représentera donc plus le nombre de processus en attente lors du premier appel à rdv, ce qui entraînera des dysfonctionnements ultérieurs.
---endCorr

Question 3 : Expériences autour d'un serveur Web (7 points + 2 points de bonus)

---beginCorr
Le corrigé des différentes sous-questions de cette question 3 ne sera mis à disposition que quand l'exercice Hors Présentiel numéro 2 aura été rendu par les étudiants.
---endCorr

Le code présent dans le répertoire Q3-1 contient un serveur Web très simplifié : quand on l'interroge, ce serveur se contente de renvoyer une page Web contenant le nombre de fois qu'on l'a interrogé (et donc qu'il a répondu).

---beginCorr
Pour information, le code de ce serveur est une adaptation du code tiré du chapitre 11 du livre Advanced Linux Programming (http://www.advancedlinuxprogramming.com/).
---endCorr

Avant de rentrer dans le vif du sujet, faites fonctionner ce serveur :
  • dans un terminal, positionnez-vous dans le répertoire Q3-1, tapez make pour compiler, puis server pour lancer l'exécution du serveur ;
    NB :
    • par défaut, le serveur se met en attente sur le port 8080 ;
    • si vous souhaitez qu'il utilise un autre numéro de port, tapez la commande :
      server -p numeroPort
    • si vous obtenez l'erreur « ./server: erreur 'Address already in use' ==> vous devez relancer le serveur en précisant un autre numéro de port avec la commande 'server -p numeroDePort' », alors relancez le serveur en précisant un autre numéro de port que le numéro que vous venez d'utiliser ;
  • dans un navigateur Web, tapez l'adresse http://localhost:8080 (ou un autre numéro de port, si vous avez démarré votre serveur avec un autre numéro de port) ;
  • le navigateur Web affiche : « compteurReponse = 1 » (le serveur indique qu'il a été interrogé une fois et donc qu'il a répondu une fois) ;
  • si vous cliquez plusieurs fois sur le bouton « Rafraîchir / Actualiser la page courante » du navigateur, votre navigateur interroge à chaque fois le serveur qui renvoie donc successivement : « compteurReponse = 2 », « compteurReponse = 3 »... ;
  • arrêtez votre serveur (à l'aide des touches Control et C).
Pour fonctionner, ce serveur utilise 4 modules : codeEtudiant.ccommon.cmain.c et  server.c. Mais, dans les questions suivantes, vous serez amené à modifier seulement codeEtudiant.c (c'est pourquoi c'est le seul module sur lequel vous avez les droits en écriture). En effet, ce module héberge 3 fonctions qui interagissent avec le reste du code :
  • la procédure init réalise toutes les initialisations de votre module avant que le serveur web ne se mette en attente de requêtes Web. Ainsi, dans le code livré dans le répertoire Q3-1, cette procédure initialise à 0 une variable appelée compteurReponse ;
  • la procédure gestion_connexion_entrante est appelée lorsque le serveur web reçoit une requête Web. Cette procédure (qui est appelée avec un paramètre connexion_fd) est chargée de confier à un thread la responsabilité d'appeler la procédure handle_connection (définie dans un des autres modules que vous n'avez pas à modifier) avec la valeur de ce paramètre connexion_fd. Ainsi, dans le code livré dans le répertoire Q3-1, cette procédure crée un thread thread_gestion_connexion_entrante (avec en paramètre la valeur de connexion_fd). Et ce thread se charge, quand il prend la main, d'appeler handle_connection avec la valeur de connexion_fd ;
  • la fonction gestion_compteurReponse est chargée d'effectuer les traitements liés à la variable compteurReponse lorsque le serveur répond à une requête HTTP. Elle modifie la valeur de compteurReponse, puis retourne la valeur courante de compteurReponse. Ainsi, dans le code livré dans le répertoire Q3-1, cette fonction incrémente compteurReponse, puis retourne sa valeur.
Les questions Q3-1 à Q3-4 sont destinées à améliorer le fonctionnement de ce serveur. Noter que seront évalués :
  • la clarté du code,
  • le fait que le code compile (sans warning),
  • le fait que le retour de chaque appel système, s'il y en a un, est testé avec appel à perror(), puis exit(EXIT_FAILURE) en cas de problème détecté,
  • le fait que le code répond correctement à la question posée.

Question 3-1 : Empêcher les pertes d'incrémentation de compteurReponse (2 points)

La variable compteurReponse peut être manipulée par plusieurs threads travaillant en parallèle. On peut donc potentiellement perdre des incrémentations de cette variable. Pour tenter de l'observer, utilisons ab, l'outil de benchmark d'Apache, qui permet d'envoyer un certain nombre de requêtes vers un serveur, dans un laps de temps très court. Effectuez les opérations suivantes :
  • dans un terminal, démarrez le serveur ;
  • dans un autre terminal, tapez la commande :
    ab -n 1000 -c 6 http://localhost:8080/
    (où 1000 désigne le nombre d'invocations au serveur et 6 désigne le nombre de connexions simultanées faites au serveur). ab vous indique les performances de votre serveur ;
  • dans un navigateur, tapez l'adresse http://localhost:8080 : à cause de ces pertes d'incrémentation potentielle, il est possible que le navigateur affiche une valeur de compteurReponse inférieure à la valeur attendue, c'est-à-dire 1001 (1000 requêtes réalisées par ab et 1 requête réalisée par votre navigateur) ;
  • arrêtez votre serveur.
Dans le répertoire Q3-1, modifiez codeEtudiant.c pour garantir qu'il n'y aura aucune perte d'incrémentation de compteurRequete.
---beginCorr
Barème :
  • clarté du code : 0,25 ;
  • compilation sans warning : 0,25 ;
  • test du retour de chaque appel système : 0,25 ;
  • réponse correcte à la question posée :
    • déclaration du mutex : 0,5 ;
    • encadrement de l'incrémentation par mutex_lock et mutex_unlock : 0,75.
voir codeEtudiant.Q3-1.corrige.c
---endCorr

Question 3-2 : Sauvegarde de compteurReponse sur disque (2,5 points)

Jusqu'à présent, lorsque l'on arrête le serveur, la valeur courante de compteurReponse est perdue. On se propose donc de mémoriser sur disque sa valeur dans un fichier sauvegardeCompteurReponse :
  • recopiez Q3-1/codeEtudiant.c dans Q3-2 ;
  • modifiez Q3-2/codeEtudiant.c de sorte que :
    • au démarrage du serveur :
      • si le fichier sauvegardeCompteurReponse n'existe pas, il est créé (avec les droits de lecture-écriture pour vous-même, le groupe et les autres, avant modification par le umask) et compteurReponse est initialisé à 0 ;
      • si le fichier sauvegardeCompteurReponse existe, compteurReponse est initialisé à la valeur mémorisée dans ce fichier ;
    • à chaque appel de gestion_compteurReponse, on sauvegarde la valeur de compteurReponse dans le fichier.
NB :
  • la valeur de compteurReponse sera stockée dans le fichier sauvegardeCompteurReponse sous forme binaire ou textuelle, selon votre préférence.
  • le fichier sauvegardeCompteurReponse sera ouvert avec ou sans l'option O_SYNC, selon votre préférence. En revanche, vous veillerez à justifier (dans un commentaire au niveau de l'instruction d'ouverture du fichier) votre décision par rapport à l'utilisation ou la non-utilisation de O_SYNC.
---beginCorr
Barème :
  • clarté du code : 0,25 ;
  • compilation sans warning : 0,25 ;
  • test du retour de chaque appel système : 0,25 ;
  • réponse correcte à la question posée :
    • création correcte du fichier : 0,5 (seulement 0,25 si les droits ne sont pas corrects) ;
    • traitement correct (lecture bien faite de la valeur de la variable) si le fichier existe déjà : 0,25 ;
    • écriture correcte de la variable : 0,5 (0,25 pour le lseek et 0,25 pour le write proprement dit) ;
    • justification par rapport à l'utilisation de O_SYNC (vu le flou de l'énoncé, on peut décider d'utiliser O_SYNC ou non) : 0,25 ;
    • encadrement de l'incrémentation et éventuellement (ce n'est pas obligatoire, car on écrit seulement 1 entier ==> Ecriture atomique) de l'écriture dans le fichier par mutex_lock et mutex_unlock : 0,25.
voir codeEtudiant.Q3-2.corrige.c pour une sauvegarde dans le fichier au format binaire.
voir codeEtudiant.Q3-2bis.corrige.c pour une sauvegarde dans le fichier au format texte.
---endCorr

Question 3-3 : Améliorer les performances de la sauvegarde sur disque (2,5 points)

En utilisant ab, on constate que le serveur réalisé à la question 3-2 voit ses performances chuter par rapport à celui de la question 3-1. C'est pourquoi, dans cette question, on met en œuvre une architecture logicielle préservant les performances de la question 3-1 tout en conservant la sauvegarde de compteurReponse abordée à la question 3-2. Le principe de cette architecture est de confier l'écriture sur disque à un thread spécifique qui écrit, chaque seconde, la valeur courante de compteurReponse dans le fichier sauvegardeCompteurReponse. Certes, si le serveur tombe, on perd le décompte de requêtes effectuées depuis la dernière seconde où ce thread spécifique s'est activé, mais on préserve les performances du serveur !
  • recopiez Q3-2/codeEtudiant.c dans Q3-3 ;
  • modifiez Q3-3/codeEtudiant.c de sorte que :
    • au démarrage du serveur :
      • si le fichier sauvegardeCompteurReponse n'existe pas, il est créé (avec les droits de lecture-écriture pour vous-même, le groupe et les autres) et compteurReponse est initialisé à 0 ;
      • si le fichier sauvegardeCompteurReponse existe, compteurReponse est initialisé à la valeur mémorisée dans ce fichier ;
      • on crée ce thread spécifique ;
    •  ce thread spécifique exécute une boucle sans fin dans laquelle :
      • il écrit la valeur courante de compteurReponse dans sauvegardeCompteurReponse ;
      • il dort pendant une seconde ;
    • à chaque appel de gestion_compteurReponse, on se contente d'incrémenter compteurReponse de 1 (sans faire aucune écriture dans le fichier sauvegardeCompteurReponse).
NB : le fichier sauvegardeCompteurReponse sera ouvert avec ou sans l'option O_SYNC, selon votre préférence. En revanche, vous veillerez à justifier (dans un commentaire au niveau de l'instruction d'ouverture du fichier) votre décision par rapport à l'utilisation ou la non-utilisation de O_SYNC.
---beginCorr
Barème :
  • clarté du code : 0,25 ;
  • compilation sans warning : 0,25 ;
  • test du retour de chaque appel système : 0,25 ;
  • réponse correcte à la question posée :
    • création du nouveau thread : 0,25 ;
    • code correct pour ce nouveau thread :
      • écriture de la valeur : 0,25 ;
      • non-encadrement de l'écriture sur disque par mutex_lock et mutex_unlock : 0,5 (pour avoir l'ensemble de ces 0,5 point, il faut recopier la valeur de compteurReponse dans une variable locale (en protégeant cette recopie via mutex_lock/mutex_unlock), puis faire l'écriture hors mutex (sinon on bloque tous les threads incrémenteurs, i.e. on n'a rien gagné par rapport à Q3-2) ;
      • sommeil : 0,25 ;
    • justification par rapport à l'utilisation de O_SYNC (on doit l'utiliser, car l'énoncé suggère qu'on ne souhaite pas perdre plus de 1 seconde d'incrémentation) : 0,25 ;
    • encadrement de l'incrémentation par mutex_lock et mutex_unlock : 0,25.
voir codeEtudiant.Q3-3.corrige.c
---endCorr

Question 3-4 (question bonus) : Utilisation d'un pool de threads (bonus de 2 points)

Dans le code actuel, à chaque requête, on crée un thread dédié, ce qui prend du temps et limite donc les performances globales du serveur.
  • recopiez Q3-3/codeEtudiant.c dans Q3-4 ;
  • modifiez Q3-4/codeEtudiant.c de sorte que :
    • au démarrage du serveur, outre les traitements mis en œuvre à la question Q3-3, on crée 20 threads qui se mettent en attente de travail ;
    • lors de l'appel à gestion_connexion_entrante, on confie le traitement de cette connexion à l'un des threads inactifs de ce pool ;
    • lorsqu'un thread a terminé son appel à handle_connection, il se remet en attente de travail.
---beginCorr
Barème :
  • Clarté du code : 0,25
  • Compilation sans warning : 0,25
  • Test du retour de chaque appel système : 0,25
  • Réponse correcte à la question posée :
    • création des 20 threads (pas forcément détachées) : 0,25
    • implémentation répondant au problème : 1 point. Pour info, dans le corrigé :
      •  on crée un tube sur lequel les 20 threads se mettent en lecture d'un connexion_fd ;
      • gestion_connexion_entrante écrit la valeur du paramètre connexion_fd sur ce tube ;
      • un seul des threads en attente arrive à lire cette valeur. Il appelle handle_connection.
voir codeEtudiant.Q3-4.corrige.c
---endCorr

Question 4 : Priorité aux rédacteurs (3 points)

NB : Cette question est difficile. Nous vous recommandons donc de ne l'aborder que quand vous aurez terminé les autres questions.

Le transparent 3.5.2 (Solution de base) du cours « Synchronisation entre processus » présente une implémentation du paradigme lecteurs/rédacteurs qui donne priorité aux lecteurs. Le transparent 3.5.4 (Solution avec priorités égales) présente une implémentation de ce paradigme qui met lecteurs et rédacteurs au même niveau. Ainsi, si on a la séquence de demande de lecture/écriture suivante :
  • demande de lecture Lecteur1,
  • demande de lecture Lecteur2,
  • demande d'écriture Rédacteur1,
  • demande de lecture Lecteur3,
  • demande d'écriture Rédacteur 2,
l'algorithme répond aux demandes de la manière suivante :
  • demande de lecture Lecteur1 accordée,
  • demande de lecture Lecteur2 accordée,
  • demande d'écriture Rédacteur1 accordée,
  • demande de lecture Lecteur3 accordée,
  • demande d'écriture Rédacteur2 accordée.
Proposez un algorithme (basé sur des appels aux fonctions P() et V()) pour que le paradigme donne systématiquement la priorité aux écrivains. Ainsi, dans le cas de la séquence de demande de lecture/écriture évoquée précédemment, cet algorithme répondra aux demandes de la manière suivante :
  • demande de lecture Lecteur1 accordée,
  • demande de lecture Lecteur2 accordée,
  • demande d'écriture Rédacteur1 accordée,
  • demande d'écriture Rédacteur2 accordée,
  • demande de lecture Lecteur3 accordée.
Pour cette question, répondez sur votre copie ou bien dans le fichier Q4/reponse4.txt (dans ce dernier cas, indiquez sur votre copie « Réponse dans Q4/reponse4.txt »).
---beginCorr
barème :
  • Toutes les variables sont là : 1 (- 0,125 par variable manquante, sauf NL_en_attente qui compte pour 0,25 à moins que l'algorithme proposé par l'étudiant permette de s'en abstenir)
  • Algorithme des lecteurs correct : 1
  • Algorithme des rédacteurs correct : 1
Variables globales
==================
Sémaphore mutexG initialisé à 1
Sémaphore mutexL initialisé à 1
Sémaphore mutexR initialisé à 1
Sémaphore mutexAttenteFinR initialisé à 1
int NL = 0
int NL_en_attente = 0
int NR = 0

Algorithme des lecteurs
=======================
P(mutexL)
NL = NL + 1
si NR >= 1 alors
// Il y a des rédacteurs qui souhaitent écrire ou qui écrivent
// On doit donc les attendre
NL_en_attente = NL_en_attente + 1
V(mutexL)
P(mutexAttenteFinR)
P(mutexL)
si NL_en_attente == NL alors
P(mutexG)
finsi
NL_en_attente = NL_en_attente - 1
sinon
si NL == 1 alors
P(mutexG)
finsi
finsi
V(mutexL)
lectures()
P(mutexL)
NL = NL - 1
si NL == 0 alors
V(mutexG)
finsi
V(mutexL)

Algorithme des rédacteurs
=========================
P(mutexR)
NR = NR + 1
V(mutexR)
P(mutexG)
ecrituresEtLectures()
V(mutexG)
P(mutexR)
NR = NR - 1
si NR == 0 alors
P(mutexL)
pour i = 1 à NL faire
P(mutexAttenteFinR)
fpouri
V(mutexL)
finsi
V(mutexR)
---endCorr



Page mise à jour le 15 avril 2009