|
|
TÉLÉCOM
SudParis 2ème
année
TP Noté CSC4508/M2 du 19/05/11
(Corrigés)
Modalités
Durée : 3 heures
Tous documents autorisés.
Les questions 1, 2 et 3 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
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 TPNote2011
NB: Si vous êtes plus à l'aise en écrivant vos
réponses sur une copie plutôt qu'en les tapant sur
ordinateur, vous
pouvez rendre également une copie papier à l'enseignant
qui vous surveille. En fin d'épreuve, même si vous ne
rendez pas de copie, passez voir l'enseignant qui vous surveille pour
lui signaler explicitement que vous ne rendez pas de copie.
Préparation
cd votreRepertoireDeTravailPourCSC4508M2 cp ~simatic/Cours/CSC4508/tPNote2011.tgz . tar xvfz tPNote2011.tgz cd TPNote2011
Question 1 : Questions de cours (4,5
points)
Pour chacune des questions ci-dessous, répondez dans le fichier Q1/reponse1.txt
ou bien sur votre
copie (dans ce
dernier
cas, indiquez dans le fichier Q1/reponse1.txt
« Réponse sur copie »).
Question 1.1 : Mémoire parent /
mémoire
enfant (3
points)
On
considère le code suivant (dans lequel toute la gestion des
codes d'erreur a été omise pour gagner en
lisibilité) :
int d1;
int *pd2;
int *pd3;
int main(){
pd2 =
malloc(sizeof(int));
shm_id = shmget(SHMKEY, sizeof(int), 0700|IPC_CREAT));
pd3 = (int *)shmat(shm_id, NULL, 0));
fork();
// Section de code concernée par les questions ci-dessous
// ------------------------------------------------------
...
}
Pour chacune des questions suivantes, vous justifierez
brièvement.
Après le fork, la
variable d1
est-elle partagée entre le parent et l'enfant qu'il a
créé ? Dit autrement, après le fork, si le
parent
change la valeur de d1,
l'enfant voit-il ce changement ?
Après le fork, la
zone de données pointée par pd2
est-elle partagée entre le parent et l'enfant qu'il a
créé ? Dit autrement, si le parent change la valeur
de *pd2, l'enfant voit-il
ce
changement ?
Même question pour la zone de donnée pointée par pd3.
---beginCorr
Barème : 1 point par proposition (0,5 pour la justesse de
la réponse
et 0,5 pour la justification), soit un total de 3
points.
d1 et *pd2
ne sont pas
partagées. En effet, la mémoire du parent est
complètement clonée pour constituer la mémoire de
l'enfant. Et ces deux zones mémoire sont étanches.
*pd3 est
partagée, puisqu'elle est stockée en mémoire
partagée.
---endCorr
Question 1.2 : Libevent et sémaphore (1,5 points)
Le
cours « Architectures Client-Serveur »
étudie la
librairie libevent. Nous
avons vu, pendant ce cours, que l'exemple de
code utilisant libevent
était limité par le nombre de
descripteurs de fichiers simultanément ouverts par le processus
serveur. Nous avons vu également qu'utiliser un sémaphore
au niveau de ce processus serveur pour limiter le nombre de clients
simultanés (et donc ne pas atteindre cette limite du nombre de
descripteurs de fichiers) n'était pas une bonne idée.
Réexpliquez pourquoi l'utilisation d'un sémaphore n'est pas une bonne idée.
---beginCorr
Barème : 1,5 point pour l'explication du problème
Au niveau du serveur, utiliser un sémaphore selon un paradigme
de synchronisation de type Cohorte n'est pas une bonne idée. En
effet, imaginons que libevent
réveille notre serveur pour lui
signaler qu'un nouveau client veut se connecter. Si le serveur fait un P(sémaphore)
et que le sémaphore est à 0 parce que le serveur a
atteint le nombre maximum de clients simultanés, le serveur se
bloque sur le P(). Par conséquent,
il bloque le thread libevent qui exécute la fonction event_dispatch() et qui est donc chargé de prendre en compte la réception des
événements en provenance de clients. De ce fait,
lorsqu'un client déjà en relation avec le serveur signale
au serveur qu'il a fini de travailler avec lui, libevent ne peut pas traiter
cette information (il est coincé dans le traitement du P()). Ainsi, le thread libevent ne peut pas
exécuter le code qui permet de faire V(sémaphore). Le serveur
est donc bloqué pour toujours.
Pour information (ce n'était pas demandé dans ce TP noté), pour
résoudre le problème, il ne faut pas employer de
sémaphore, mais une variable contenant le nombre de connexions
que le serveur peut encore accepter. Cette variable est
initialisée au nombre maximum
de connexions que le serveur peut accepter. À chaque fois que le
serveur
traite la
connexion d'un nouveau client, il décrémente cette
variable.
Si cette variable arrive à 0, le serveur désactive la
prise en compte de l'événement ArrivéeNouveauClient au
niveau de libevent (il faut donc retirer l'attribut EV_PERSIST au niveau de l'instruction event_set(&eventTubePrincipal, fdR, EV_READ|EV_PERSIST, callbackTubePrincipal, NULL); et ne pas faire d'instruction event_add(&eventTubeprincipal, NULL); dans la fonction callbackTubePrincipal()) Ainsi
les nouveaux clients qui veulent se connecter voient leur demande de
connexion mises en attente par libevent.
Quand un client déjà connecté a fini de travailler
avec le serveur, ce dernier incrémente la variable et signale
à libevent qu'il
accepte désormais de prendre à nouveau en compte les
événements ArrivéeNouveauClient.
---endCorr
Question 2 : La Trace du Hourra (8,5 points)
Pour chacune des questions ci-dessous :
- Répondez dans le fichier
Q2/reponse2.txt
ou bien sur votre
copie (dans ce
dernier
cas, indiquez dans le fichier Q2/reponse2.txt
« Réponse sur copie »).
- Le langage algorithmique utilisé n'a pas besoin de
respecter strictement la syntaxe apprise en première
année. L'essentiel est qu'il soit lisible par le correcteur et
sans ambiguïté.
Au Parc d'attractions Astérix, « La Trace du
Hourraanbsp;»
est une montagne russe avec 3 trains qui circulent simultanément
sur un circuit. Cet exercice a pour objectif d'écrire les
algorithmes de synchronisation qui permettraient de gérer
automatiquement cette montagne russe.
La figure 1 modélise cette montagne russe. Elle est
constituée de 4 zones:
- zoneE est la
zone d'Embarquement où les passagers montent et descendent d'un
train. zoneE peut
contenir au maximum 2 trains.
- zoneA est la
zone d'Attente juste avant de pouvoir entrer dans zoneE. Elle ne peut contenir
qu'un train.
- zoneH est la
zone où un train monte avant de se lancer dans sa
descente infernale (c'est donc la zone des Hurlements où les
gens
crient « Help, je veux descendre ! Non, pas comme
çaaaaaaaa... »). Elle ne peut contenir qu'un train.
- zoneG est la
zone de Garage du train. Les trains y sont parqués avant
d'être injectés dans le circuit zoneE-zoneH-zoneA. NB : dans cet
exercice, on suppose qu'une
fois qu'un train est sorti de zoneG,
il n'y retourne plus jamais.
Figure 1 : Modélisation de la montagne russe
« La Trace du
Hourra »
Quand cette attraction fonctionne :
- Un train ne peut quitter zoneG que s'il y a de la place
dans zoneE.
- Un train ne peut quitter zoneE que si zoneH est vide.
- Un train ne peut quitter zoneH que si zoneA est vide.
- Un train ne peut quitter zoneA que s'il y a de la place
dans zoneE.
Question 2.1 : Paradigmes de synchronisation des trains sur la Trace du Hourra (1,5 point)Indiquez tous les paradigmes de synchronisation présents dans cette montagne russe.
---beginCorr
Barème : 0,5 point par paradigme * 3 paradigmes = 1,5 point
L'énoncé indique que, quand cette attraction fonctionne,
il y a 4 conditions de sortie de zone. Ces 4 conditions de sortie se
ramènent à 3 conditions d'entrée dans un zone :
- Qu'il vienne de zoneG ou zoneA, un train ne peut rentrer dans zoneE que si l'une des 2 places de zoneE est libre. C'est donc un paradigme de type Cohorte.
- Un train ne peut quitter zoneE que si zoneH est vide, donc s'il a la garantie qu'il sera seul dans cette zone. C'est donc un paradigme de type Exclusion mutuelle.
- Un train ne peut quitter zoneH que si zoneA est vide, donc s'il a la garantie qu'il sera seul dans cette zone. C'est donc un paradigme de type Exclusion mutuelle.
---endCorr
Question 2.2 : Algorithme de synchronisation des trains sur la Trace
du Hourra
(5 points)
Chaque train est modélisé par un processus
exécutant l'algorithme codeTrain()
suivant :
procédure codeTrain()
// à compléter, si
besoin, avec du
code de synchronisation
codeLiéAlaPrésenceDansZoneG();
// à compléter, si
besoin, avec du
code de synchronisation
Tant que VRAI faire
// à compléter, si
besoin, avec du
code de synchronisation
codeLiéAlaPrésenceDansZoneE();
// à
compléter, si
besoin, avec du code de
synchronisation
codeLiéAlaPrésenceDansZoneH();
// à
compléter, si
besoin, avec du code de
synchronisation
codeLiéAlaPrésenceDansZoneA();
// à
compléter, si
besoin, avec du code de
synchronisation
Fin Tant que
Fin Procédure
À
l'aide de sémaphores (dont vous indiquerez les valeurs
initiales), d'éventuelles variables additionnelles (dont vous
indiquerez les valeurs initiales), et d'opérations P() et V(), complétez
l'algorithme de codeTrain().
---beginCorr
Barème :
- 4 points répartis en 1 point par contrainte correctement traitée (d'un point de vue synchronisation) :
- Un train ne peut quitter zoneG que s'il y a de la place
dans zoneE.
- Un train ne peut quitter zoneE que si zoneH est vide.
- Un train ne peut quitter zoneH que si zoneA est vide.
- Un train ne peut quitter zoneA que s'il y a de la place
dans zoneE.
- 1 point pour la notion de vientDeZoneG
Semaphore zoneE
initialisé à 2;
Semaphore zoneA
initialisé à 1;
Semaphore zoneH
initialisé à 1;
Procédure codeTrain()
codeLiéAlaPrésenceDansZoneG();
P(zoneE);
Tant que VRAI faire
codeLiéAlaPrésenceDansZoneE();
P(zoneH)
V(zoneE);
codeLiéAlaPrésenceDansZoneH();
P(zoneA);
V(zoneH);
codeLiéAlaPrésenceDansZoneA1();
P(zoneE);
V(zoneA);
Fin Tant que
Fin Procédure
La solution suivante (moins performante que la précédente
puisqu'on fait systématiquement un test à chaque boucle)
s'appuie sur un booléen vientDeZoneG:
Semaphore zoneE
initialisé à 2;
Semaphore zoneA
initialisé à 1;
Semaphore zoneH
initialisé à 1;
Procédure codeTrain()
Booléen vientDeZoneG = VRAI codeLiéAlaPrésenceDansZoneG();
Tant que VRAI faire
P(zoneE);
Si vientDeZoneG == VRAI Alors
// Ce test est dû au fait que
lorsqu'on lance un
// processus train pour la première fois, le train
// sort du garage
où il était stocké pour aller
// directement dans la
zone d'embarquement (il
ne
// sort donc pas de la zone A).
vientDeZoneG = FAUX
Sinon
V(zoneA);
Fin Si
codeLiéAlaPrésenceDansZoneE();
P(zoneH)
V(zoneE);
codeLiéAlaPrésenceDansZoneH();
P(zoneA);
V(zoneH);
codeLiéAlaPrésenceDansZoneA1();
Fin Tant que
Fin Procédure
---endCorr
Question 2.3 : Algorithme d'embarquement et de
débarquement des passagers (2
points)
NB : cette question est plus difficile. Nous vous recommandons de ne
l'aborder qu'en fin de TP noté.
Quand un train entre dans zoneE,
il stationne sur la voie voie0
si celle-ci est libre, et sur la voie voie1
sinon. Ensuite, ce train autorise ses passagers à
débarquer. Une fois qu'il est vide, le train autorise les
usagers en attente d'embarquement sur cette voie à embarquer.
Quand le train est plein, il quitte zoneE.
Par ailleurs, quand un usager de l'attraction entre dans zoneE, il commence par choisir
la voie (voie0 ou voie1)
où il souhaite embarquer. Une fois embarqué dans un
train, il attend d'être autorisé à débarquer
du train. Voici son algorithme:
Procédure codeUsager()
Entier numéroTrain
voieChoisiePourEmbarquer =
nombreAléatoireEntre_0_et_1
numéroTrain =
embarquerDansTrain(voieChoisiePourEmbarquer);
débarquerDeTrain(numéroTrain);
Fin Procédure
Sachant qu'un train peut contenir au maximum N passagers, écrivez les
algorithmes de embarquerDansTrain(),
débarquerDeTrain()
et codeLiéAlaPrésenceDansZoneE()
évoqué à la question 2.1. Vous utiliserez des
sémaphores (dont vous indiquerez les valeurs initiales),
d'éventuelles variables additionnelles (dont vous indiquerez les
valeurs initiales), et d'opérations P() et V().
Si vous en avez besoin (parce que, par exemple, du code de
synchronisation de train écrit à la questionQ2.1 doit se
retrouver dans codeLiéAlaPrésenceDansZoneE()),
écrivez une nouvelle version de codeTrain().
---beginCorr
Barème :
- 2 points répartis en 0,5 point par algorithme à écrire
Structure Voie
// Variable indiquant si la voie est libre (cas VRAI) ou non (cas FAUX)
Booléen libre = VRAI
//
numéro du train actuellement stocké sur la voie (cas
où libre==VRAI)
Entier numTrain // Variable pour la gestion de l'attente, par les usagers d'une voie,
// de pouvoir embarquer dans le train qui s'arrêtera sur cette voie
// (comme on ne sait pas quel est le train qui s'arrêtera sur la voie,
// ce sémaphore ne peut être qu'au niveau de la voie).
Sémaphore
semEmbarquement initialisé à 0
Fin Structure
Voie voie[NB_VOIES]
// Gestion de la concurrence d'accès au tableau voie[]
Sémaphore mutexVoie
initialisé à 1 // Permet de gérer la concurrence
d'accès au tableau voie[]
Structure Train
// Variable pour la gestion de l'attente, par les passagers, de
// pouvoir débarquer du train
Sémaphore
semDebarquement initialisé à 0
// Variables pour la gestion de l'attente, par le train, que tous
// les passagers aient embarque dans le train
Sémaphore
mutexNbPassagersEmbarques initialisé à 1
Entier nbPassagersEmbarques = 0
Sémaphore semPassagersTousEmbarques = 0
// Variables pour la gestion de l'attente, par le train, que tous
// les passagers aient debarque d'un train
Sémaphore
mutexNbPassagersDebarques initialisé à 1
Entier nbPassagersDebarques = 0
Sémaphore semPassagersTousDebarques = 0
Booléen vientDeZoneG = VRAI
Fin Structure
Train train[NB_TRAINS]
Procédure
codeLiéAlaPrésenceDansZoneE()
// Recherche d'une voie libre
P(mutexVoie)
Si voie[0].libre == VRAI
alors
maVoie = 0
Sinon
maVoie = 1
Fin Si
voie[maVoie].libre = FAUXvoie[maVoie].numTrain =
numTrainDuProcessusActuel
V(mutexVoie)
// Le train autorise ses
passagers actuels à débarquer
Si train[numTrainDuProcessusActuel].vientDeZoneG == VRAI Alors
// Ce test est dû au fait que
lorsqu'on lance un processus train pour la première fois,
// le train sort du garage
où il était stocké pour aller directement dans la
zone
// d'embarquement (il ne
sort donc pas de la zone A) et il n'a pas de passagers.
train[numTrainDuProcessusActuel].vientDeZoneG = FAUX
Sinon
// Le train ne venant pas de zoneG, il contient des passagers. On les débarque.
Répéter N fois
P(voie[maVoie].semDebarquement)
Fin Répéter
Fin Si
// Le train attend que ses
passagers actuels aient tous débarqué
P(train[semPassagersTousDebarques].semPassagersTousDebarques)
// Le train autorise N
passagers en attente sur sa voie à embarquer
Répéter N fois
P(voie[maVoie].semEmbarquement)
Fin Répéter
// Le train attend que tous
les passagers aient embarqué
P(train[semPassagersTousDebarques].semPassagersTousEmbarques)
// Le train quitte zoneE
P(zoneH)
P(mutexVoie)
voie[maVoie].libre = VRAI
V(mutexVoie)
V(zoneE)
Fin Procédure
Fonction
embarquerDansTrain(donnée voieChoisiePourEmbarquer) retourne
entier
Entier numTrain
// On attend de pouvoir embarquer dans un train
P(voie[voieChoisiePourEmbarquer].semEmbarquement)
// On signale au train si on est le dernier passager à embarquer
numTrain = voie[voieChoisiePourEmbarquer].numTrain
P(train[numTrain].mutexNbPassagersEmbarques)
train[numTrain].nbPassagersEmbarques
+= 1
Si
train[numTrain].nbPassagersEmbarques >= N Alors
train[numTrain].nbPassagersEmbarques = 0
P(train[numTrain].semPassagersTousEmbarques)
Fin Si
V(train[numTrain].mutexNbPassagersEmbarques)
retourne numTrain
Fin Fonction
Procédure
débarquerDeTrain(Donnée : Entier numTrain)
Entier numVoie
// On attend que le train nous autorise à débarquer
P(train[numTrain].semDebarquement)
// On signale au train si on est le dernier passager à débarquer
P(train[numTrain].mutexNbPassagersDebarques)
train[numTrain].nbPassagersDebarques
+= 1
Si
train[numTrain].nbPassagersDebarques >= N Alors
train[numTrain].nbPassagersDebarques = 0
P(train[numTrain].semPassagersTousDebarques)
Fin Si
V(train[numTrain].mutexNbPassagersDebarques)
Fin procédure
codeTrain() ne doit évoluer que si on a utilisé un booléen vientDeZoneG à la question
Q2.1. En effet, dans ce cas, le V(zoneA) doit être intégré à codeLiéAlaPrésenceDansZoneG().
---endCorr
Question 3 : Multi-threading pour un serveur de liste de M. et Mme (7 points)
NB
: Pour certaines des questions ci-dessous, vous êtes
conviés à répondre dans le fichier Q3/reponse3.txt
. Si vous préférez répondre sur votre
copie, indiquez dans le fichier Q3/reponse3.txt
« Réponse sur copie ».
Dans cette question, on considère un serveur de liste de M. et
Mme. Il permet à un processus client de récupérer
la liste des M. et Mme stocké dans un fichier mEtMme.txt
géré par un processus serveur.
Préliminaire
Avant de rentrer dans le vif du sujet, faites fonctionner cette
application :
- dans un terminal (noté TermS dans la
suite), positionnez-vous dans le
répertoire Q3,
tapez make pour compiler,
puis ./serveur
- dans un autre terminal (noté TermC dans
la suite), positionnez-vous dans le
répertoire Q3,
tapez ./client -v nomClient
Le client affiche :
Client nomClient : Connexion au
serveur...
Client nomClient : Connecte !
Client nomClient : Recuperation liste des M. et Mme...
Client nomClient : Nouveau M. et Mme = ABA
ont un fils : Bart
...
Client nomClient : Nouveau M. et Mme =
ZOUDANLCOU
ont une fille Debby
Client nomClient : Recuperation terminee !
- pendant ce temps, le serveur affiche dans TermS :
1-ieme client a traiter (point
d'access client = ./serveur-client.3565)
Pour accomplir ce travail, le client se connecte via un
tube nommé client-serveur
commun à tous les clients du serveur. Puis, le client envoie un
message CONNEXION
indiquant le tube nommé serveur-client.pidClient
sur lequel le client attend des réponses du serveur. Le serveur
crée un tube nommé client-serveur.rangClient
(qui sera spécifique aux échanges entre ce client et le
serveur) et renvoie le message CONNEXION_OK
(sur le tube serveur-client.pidClient)
avec le nom du tube client-serveur.rangClient.
Le client affiche « Connecte
! »
Ensuite, le client envoie, via le tube client-serveur.rangClient, un
premier message REQUETE.
Le
serveur répond, via le tube serveur-client.pidClient, le
message REQUETE_OK avec le premier M. et Mme du fichier mEtMme.txt. Si le client a
été lancé avec l'option -v, il affiche le M. et Mme
reçu.
Le client envoie alors un deuxième message REQUETE auquel le
serveur répond avec le M. et Mme suivant du fichier.
Et ainsi
de suite jusqu'à ce que le serveur renvoie au client le message REQUETE_KO pour
signifier qu'il a envoyé au client tous les M. et Mme du
fichier. Le client affiche alors « Recuperation terminee ! »
Les tubes serveur-client.pidClient
et client-serveur.rangClient
sont alors détruits. Le client s'arrête.
Travail à faire
Le serveur est actuellement mono-activité, de sorte qu'il ne
peut traiter qu'un client à la fois.
Ainsi, si vous tapez dans TermC: for ((i=1;i<=3;i+=1)); do (./client
$i &);done
vous obtenez l'affichage :
$ for ((i=1;i<=3;i+=1)); do
(./client $i &);done
Client
1 : Connexion au serveur...
Client 1
: Connecte !
Client
1 : Recuperation liste des M. et Mme...
Client
2 : Connexion au serveur...
Client
3 : Connexion au serveur...
Client
1 : Recuperation terminee !
Client
2 : Connecte !
# Le serveur a enfin répondu à la
demande de
connexion
# de Client 2, puisqu'il a fini de traiter la demande
# de liste faite par Client 1
Client
2 : Recuperation liste des M. et Mme...
Client
4 : Connexion au serveur...
Client
2 : Recuperation terminee !
Client 3
: Connecte !
# Le serveur a enfin répondu à la
demande de
connexion
# de Client 3, puisqu'il a fini de traiter la demande
# de liste faite par Client 2
traiter la demande de liste du Client 1
Client
3 : Recuperation liste des M. et Mme...
Client
3 : Recuperation terminee !
Ajoutez du multi-threading à Q3/serveur.c de sorte que le
serveur
puisse gérer plusieurs clients simultanément. Vous veillerez notamment à :
- protéger la section de code comprise rangClient++; printf(); f = fopen(); if (f == NULL); sprintf(nomTube, "%s.%d", NOM_POINT_ACCES_SERV,rangClient); par une exclusion mutuelle;
- expliquer, dans le fichier Q3/reponse3.txt, la raison pour laquelle il faut mettre cette exclusion mutuelle.
Vérifier que votre serveur gère désormais bien 3
clients en parallèle en tapant dans TermC: for ((i=1;i<=3;i+=1)); do (./client
$i &);done
Vous devez obtenir un affichage montrant qu'il y a désormais
parallélisme entre les clients comme, par exemple, dans la trace
suivante :
$ for ((i=1;i<=3;i+=1)); do
(./client $i &);done
Client
1 : Connexion au serveur...
Client 2
: Connexion au serveur...
Client 1
: Connecte !
Client
1 : Recuperation liste des M. et Mme...
Client
2 : Connecte !
Client
2 : Recuperation liste des M. et Mme...
Client
3 : Connexion au serveur...
Client 3
: Connecte !
Client
3 : Recuperation liste des M. et Mme...
Client
2 : Recuperation terminee !
Client
1 : Recuperation terminee !
Client
3 : Recuperation terminee !
Pour information, au delà de 299 clients
simultanés, votre serveur s'arrête pour une raison qui sort du cadre de ce TP noté.
---beginCorr
Barème :
- Création correcte des threads : 1,5
point
- Terminaison correcte du thread
(et le fait qu'il est
détaché du thread principal pour mourir de sa belle mort)
: 1 point (0,5 pour pthread_exit et 0,5 pour pthread_detach)
- Gestion correcte de la mémoire contenant le
message initial : 1,5 point (0,5 point pour l'allocation, 0,5 pour
le
passage en paramètre pendant la création du thread, et
0,5
point pour la libération)
- Test
sur code retour de pthread_create et de malloc :
1
point (0,5 point pour chaque) auquel peuvent se retirer (on peut donc
avoir une note négative pour cette rubrique) 0,5 pour une
indentation moche et 0,5 point pour une compilation avec Warning.
- Définition du mutex pour l'exclusion mutuelle et positionnement correct du P et du V : 1 point
- Justification de l'exclusion mutuelle : 1 point
Justification de l'exclusion mutuelle : dans la section de code rangClient++; printf(); f = fopen();
if (f == NULL); sprintf(nomTube, "%s.%d",
NOM_POINT_ACCES_SERV,rangClient); , 2 threads peuvent exécuter presque simultanément rangClient++. De ce ce fait, quand il exécutent sprintf(nomTube, "%s.%d",
NOM_POINT_ACCES_SERV,rangClient),
ils peuvent potentiellement utiliser la même valeur de
rangClient. Donc, le serveur utilisera le même tube nommé
pour communiquer avec 2 clients différents. Pour éviter
ce problème, il faut
protéger cette section de code par une exclusion mutuelle.
Voir fichier serveur.Q3.corrige.c
---endCorr
|