|
|
Entrées-sorties
(Corrigés)
Exercice 1 : Nombre d'octets à écrire et
performances
Préparation
Se placer dans un répertoire de travail
tar xvfz perfW.tgz
cd PerfW
Question 1
Compléter toutes les sections "/* A vous de compléter */"
du fichier pw.c
make
---beginCorr
pw.corrige.c
---endCorr
Question 2
Ca y est : "pw.c compile" !
/usr/bin/time pw 16
Puis 32, 64 et toutes les puissances de 2 jusqu'à 8192. Pour
automatiser ce traitement, on peut s'appuyer sur les capacités
de bash:
- for i in 16 32 64 128 256 512 1024 2048 4096 8192; do echo $i; /usr/bin/time pw $i; done
- ou bien
for ((i=16; i<= 8192; i*=2)); do echo $i; /usr/bin/time pw $i; done
Qu'en concluez-vous ?
---beginCorr
Plus les blocs utilisés pour l'écriture sont gros, plus
ça va vite et surtout moins on utilise la CPU. A partir d'une
certaine taille de bloc, on atteint une asymptote.
---endCorr
Question 3
Rajouter l'option O_SYNC à votre fonction open()
/usr/bin/time pw 512
/usr/bin/time pw 8192
Qu'en pensez-vous ?
---beginCorr
Les performances chutent de manière dramatique. En revanche,
avec O_SYNC, on a la garantie que, lorsqu'on revient du write, les
octets sont physiquement présents sur le disque.
---endCorr
Question 4
Recopier pw.c en pwWritev.c
Modifier pwWritev.c en :
- Retirant l'option O_SYNC de la fonction open(),
- Ecrivant le fichier à l'aide de la fonction writev (au lieu
de write). writev devra être appelé avec, en paramètre,
8192/param_appel_a_pwWritev structures
iovec pointant sur des zones de param_appel_a_pwWritev octets.
Compiler pwWritev
Comparer les performances de pw 16, pw 8192, pwWritev 16, et pwWritev 8192
---beginCorr pwWritev.corrige.c
Voici les performances obtenues sur une machine équipée
d'un processeur Intel Core 2 Duo CPU E4500 @ 2.20GHz, munie de 2
Go de RAM
$ /usr/bin/time pw 16
0.65user 13.95system 0:14.62elapsed 99%CPU (0avgtext+0avgdata 1424maxresident)k
0inputs+278528outputs (0major+122minor)pagefaults 0swaps
$ /usr/bin/time pw 8192
0.00user 0.23system 0:00.71elapsed 32%CPU (0avgtext+0avgdata 1456maxresident)k
0inputs+332704outputs (0major+139minor)pagefaults 0swaps
$ /usr/bin/time pwWritev 16
0.00user 0.48system 0:00.78elapsed 61%CPU (0avgtext+0avgdata 1472maxresident)k
0inputs+524824outputs (0major+125minor)pagefaults 0swaps
$ /usr/bin/time pwWritev 8192
0.00user 0.27system 0:00.60elapsed 44%CPU (0avgtext+0avgdata 1440maxresident)k
0inputs+762312outputs (0major+123minor)pagefaults 0swaps
pwWritev 16
écrit avec des blocs de 16 octets. Mais comme ces blocs sont
écrits par "gros" paquets représentant 8192 octets. pwWritev 16 a des performances similaires à celle de pw 8192 , soit un gain de 95% en temps d'exécution par rapport à pw 16. pwWritev 8192 est plus performant (en temps d'exécution) que pw 8192.
Mais cela est peut-être dû à une imprécision
dans les mesures qui auraient dues être faites plusieurs fois.
---endCorr
Exercice 2 : Comment écrire par blocs de 8192 octets
alors que l'application écrit par blocs de 16 octets et ce qu'on
y gagne
Préparation
Se placer dans un répertoire de travail
tar xvfz cmpW.tgz
cd CmpW
Question 1
Dans cw.c, comparer ecritMethode1() et ecritMethode2().
Pourquoi à la fin de ecritMethode2() doit-on appeler flushBuf() ?
---beginCorr
Ces 2 fonctions écrivent un fichier au même contenu, mais
de manière différente :
- ecritMethode1() écrit le fichier /var/tmp/ficTest1 en
écrivant 16 octets par 16 octets, physiquement dans le fichier.
- ecritMethode2() écrit le fichier /var/tmp/ficTest2 en
écrivant 16 octets, par 16 octets et en passant par un buffer de
manière à n'écrire physiquement dans le fichier
que tous les 8192 octets. FlushBuf() garantit qu'il n'y pas d'octets
qui reste dans ce buffer quand on a fini d'écrire et qu'on se
prépare à fermer le fichier.
---endCorr
Question 2
On souhaite comparer ces 2 méthodes d'écriture. Pour ce
faire, on va "instrumenter" (faire un "profiling", si on utilise la
terminologie anglaise) le programme cw.
Taper
make cw
Que fait cette instruction ?
Noter que :
- L'option -pg qui demande l'instrumentation
Taper
make run
Que fait cette instruction ?
Noter que:
- "/usr/bin/time cw" exécute le programme et
génère un fichier contenant les résultats du
"profiling" : gmon.out
- "gprof cw > resultat.txt" génère une
version compréhensible par un humain du fichier gmon.out,
génération qu'on redirige vers le fichier resultat.txt
Voici un exemple commenté du fichier resultat.txt
(les commentaires sont signalés par des "//"). Consulter le pour
voir le type d'informations qu'on peut exploiter avec ce profiling.
Analyser (rapidement) votre propre fichier resultat.txt.
---beginCorr
make cw compile le programme en l'instrumentant.
"make run" exécute cw et génère resultat.txt
---endCorr
Question 3
Mêmes manipulations qu'à la question 2 en ajoutant O_SYNC
au moment du open des fichiers *et* en diminuant
d'au moins un
facteur 50 la taille des fichiers tests générés.
---beginCorr
À compléter
---endCorr
Exercice 3 : Calcul du nombre de lignes
- Écrire un programme qui lit un fichier et affiche
le nombre de lignes de ce fichier.
- Compléter ce programme pour que le nom du fichier
soit
passé en argument.
- Ajouter un test sur la nature de cet argument : s'il
s'agit d'un fichier, compter et afficher son nombre de lignes, sinon,
afficher seulement son type : répertoire ou fichier
spécial.
- Compléter ce programme de la façon suivante
: quand l'argument est un répertoire, il faut alors se
déplacer sous ce répertoire, et afficher le nombre de
lignes de tous les fichiers ordinaires qu'il contient et seulement le
nom pour les autres entrées.
Remarque : il faut penser à tester l'exécution des
fonctions (par exemple si le répertoire est inaccessible, la
fonction chdir échoue).
---beginCorr
nb_lignes.corrige.c
---endCorr
Exercice 4 : Informations SGF
- Écrire un programme qui sera exécuté
avec en argument un nom de fichier ou de répertoire. Ce
programme affichera alors le nombre de blocs total, le nombre de blocs
libres, le nombre d'inodes total et le nombre d'inodes libres du
système de fichiers auquel appartient cet argument.
Les résultats de ce programme pourront être
vérifiés avec les résultats de la commande df.
(attention : selon les versions, df utilise des
blocs de 512 ou
de 1K octets).
---beginCorr
info_sgf.corrige.c
---endCorr
Exercice 5 : Méthode pour trouver
"instantanément" la i-è ligne d'un fichier texte
On se propose de développer un programme gl.c
(pour GotoLigne) permettant de trouver "instantanément" la ième
ligne d'un fichier texte dont les lignes ont des longueurs
différentes (inférieures à 1024 caractères,
caractère de nouvelle ligne inclus).
Le principe de la solution retenue est le suivant pour trouver la iè
ligne du fichier mEtMme.txt
:
- Avant l'utilisation de
gl.c ,
on construit
(à l'aide du programme d'initialisation gli )
un
fichier mEtMme.txt.idx tel que :
- Les 4 premiers octets du fichier
mEtMme.txt.idx
contiennent le décalage écrit en binaire de la 1ère ligne de mEtMme.txt
(par rapport au début de mEtMme.txt), i.e. 0x00000000 (zéro).
- Les 4 suivants contiennent le décalage de la 2ème
ligne de
mEtMme.txt , i.e. longueur de la 1ère
ligne. Par exemple, 0x07000000 (et non 0x00000007 , car le processeur Intel est Little Endian ; NB: vous n'aurez rien de spécial à faire dans votre programme pour prendre en compte le Little Endian) si la 1ère
ligne
de mEtMme.txt contient "Coucou\n".
- Les 4 suivants contiennent le décalage de la 3ème
ligne de
mEtMme.txt , i.e. longueur de la 1ère
ligne + longueur de la 2ème. Par exemple, 0x0d000000 ,
si la 2ème ligne contient "Hello\n".
- Au moment de l'exécution de
gl.c
:
- On ouvre le fichier
mEtMme.txt.idx
- Grâce à
lseek on se décale pour se placer au niveau des octets
correspondant au décalage du numéro de ligne
cherchée
- On lit, dans un entier, les 4 octets correspondant
au
numéro de ligne cherchée
- On lit également les 4 octets correspondant au
numéro de ligne recherché + 1 (on connaît ainsi la
longueur de la ligne cherchée)
- On ouvre le fichier
mEtMme.txt
- On se décale de la valeur des 4 octets
correspondant à la ligne cherchée
- On lit le fichier pour récupérer toute la
ligne
Question 1
Écrire le programme d'initialisation "gli" (à l'aide des
fonctions de la bibliothèque C d'entrées-sorties). Ce
programme lit sur la ligne de commande le nom du fichier pour lequel il
faut générer le fichier d'index (le nom du fichier
d'index est le nom du fichier original concaténé avec
".idx").
---beginCorr
gli.corrige.c
---endCorr
Question 2
Écrire le programme "gl" (à l'aide des fonctions de la
bibliothèque C d'entrées-sorties). Ce programme lit sur
la
ligne de commande le nom du fichier et le numéro de ligne. Il
affiche
la ligne.
---beginCorr
gl.corrige.c
---endCorr
Question 3
Écrire une nouvelle version de "gl" (glMmap) qui utilise mmap
pour permettre la lecture des octets du fichier d'index sans utiliser
fread ou read.
---beginCorr
glMmap.corrige.c
---endCorr
Exercice 6 : Accès concurrents et NFS
Question 1
Écrire un programme "nfs" qui :
- Lit sur la ligne de commande :
- le nom du fichier à ouvrir
- une chaîne de caractère(s).
- Ouvre le fichier en mode écriture seulement,
création si n'existe pas et mode "ajout" (droits
lecture/écriture pour l'utilisateur seulement).
- Écrit 10.000.000 de fois la chaîne de
caractères dans le fichier.
---beginCorr
nfs.c
---endCorr
Question 2
Lancer la commande "nfs /var/tmp/ficTest a" sur 2 terminaux
différents en parallèle. Faire "ls -l /var/tmp/ficTest".
Que pensez-vous de la taille du fichier obtenu ? Effacer le fichier
/var/tmp/ficTest
---beginCorr
Le fichier obtenu fait 20.000.000 de caractères, ce qui est
normal vu que l'un des programmes a écrit 10.000.000 de
caractères et que l'autre aussi.
---endCorr
Question 3
Même opération avec le fichier ~/ficTest avec:
- les 2 terminaux étant sur la même machine (ne
pas
oublier d'effacer le fichier obtenu) : pourquoi a-t-on le même
comportement qu'à la question 1 ?
- les 2 terminaux sur une machien différente :
pourquoi perd-on des écritures ?
---beginCorr
Dans le premier cas, le fichier obtenu fait 20.000.000 de
caractères, ce qui est normal vu que l'un des programmes a
écrit 10.000.000 de caractères, que l'autre aussi et que
les deux programmes passent par le même serveur NFS (vus qu'ils
sont sur la même machine).
En revanche, dans le second cas, des écritures sont perdus ! Par
exemple, l'auteur de l'exercice a obtenu une taille finale de
11.075.971
octets. En effet, NFS ne forwarde pas immédiatement ses
écritures
au serveur ==> Les écritures de l'un des programmes peuvent
écraser les écritures d'un autre programme.
---endCorr
Exercice 7 : Gestion d'une banque
Préparation
Se placer dans un répertoire de travail
tar xvfz banque.tgz
cd Banque
---beginCorr
0,25 points sont attribués si l'indentation des différents sources est correcte.
0,25 points sont attribués si les trois codes compilent sans warning (ni erreur). 0,5 points seront attribués si les types sont corrects (double) et les pointeurs aussi.
---endCorr
RecommandationDans cette exercice, vous allez écrire du
code. Et ce code, vous allez le tester pour le vérifier. Il est
recommandé d'effecturer ces tests avec un numéro de
compte autre que le compte numéro 0 (zéro) pour
éviter de passer à côté de certaines erreurs
(impossibles à voir avec le compte numéro 0).Question 0
On souhaite écrire un programme pour la gestion d'une banque. Ce
programme manipule un fichier de données ("banque.dat") contenant
NBCOMPTES comptes en banque. Chaque compte est identifié par un
numéro correspondant au rang du compte dans le fichier (par
exemple, le compte numéro 0 apparaît en premier dans le fichier) et est
représenté dans le fichier par un nombre décimal (éventuellement
négatif) correspondant au solde du compte.
Dans cette question, on suppose que NBCOMPTES vaut 4 et que
les 4 comptes ont respectivement pour solde : -10,02 euros, 1235,43
euros, 150000,43 euros et 9999999,99 euros.
On considère les trois implémentations suivantes du fichier :
- Implémentation 1 :
-10.02[caractère(s)
de renvoi à la ligne]
1235.43[caractère(s)
de renvoi à la ligne]
150000.43[caractère(s)
de renvoi à la ligne]
9999999.99[caractère(s)
de renvoi à la ligne]
- Implémentation 2 :
-10.02[caractère(s) de renvoi à la ligne]
1235.43[caractère(s) de renvoi à la ligne]
150000.43[caractère(s)
de renvoi à la ligne]
9999999.99[caractère(s)
de renvoi à la ligne]
- Implémentation 3 :
XXXXXXXXYYYYYYYYZZZZZZZZTTTTTTTT
où X (respectivement Y, Z et T) représente 1 octet, XXXXXXXX
(respectivement YYYYYYYY, ZZZZZZZZ et TTTTTTTT) correspondant à la
valeur binaire du double contenant le solde du compte numéro 0
(respectivement 1, 2 et 3).
Dans le fichier reponses.txt, calculez (en justifiant brièvement) pour chacune de ces implémentations :
- Le nombre d'octets requis pour stocker les 4 comptes
(pour les implémentations 1 et 2, on distinguera le cas Unix et le cas
Windows)
- Le nombre d'octets qui doivent être lus sur disque
pour disposer en mémoire du solde du compte numéro 3 (traiter uniquement le cas Unix).
---beginCorr
Barême : 1 point pour les calculs (et justification) liés
à chaque implémentation, soit un total de 3 points.
- Implémentation 1 :
- Sous Unix, on a besoin de : 7 + 8 + 10 + 11 = 36
octets. Sous Windows, 4 octets de plus sont nécessaires, car le renvoi à la ligne est
codé sur 2 octets (CarriageReturn suivi de LineFeed).
- Dans cette implémentation, comme les différentes
lignes n'ont pas la même dimension, on est obligé de lire
les lignes qui précèdent celle qu'on souhaite lire. On
aura donc besoin de lire 36 octets. NB : on a besoin de lire le
caractère de renvoi à la ligne final pour savoir que la
lecture de la ligne (et donc du flottant) est terminée.
- Implémentation 2 :
- Sous Unix, on a besoin de : 11 + 11 + 11 + 11 = 44
octets. Sous Windows, 4 octets de plus sont nécessaires, car le renvoi à la ligne est
codé sur 2 octets (CarriageReturn suivi de LineFeed).
- Dans cette implémentation, comme les différentes
lignes ont la même dimension, pour accéder au compte
numéro 3, on peut
faire un seek (ou un fseek, selon la librairie qu'on utilise) pour se
positionner sur le 3x11=33è octet du fichier et ainsi lire
directement
la ligne correspondant au compte numéro 3, soit 10 octets (on
peut se passer de lire le caractère de renvoi à la ligne
puisqu'on sait qu'il est forcément en 11ème position).
- Implémentation 3 :
- On a besoin de : 8 + 8 + 8 + 8 = 32 octets.
- Dans cette implémentation, comme les différentes
comptes ont la même
dimension (ce sont des double stockés en binaire), pour accéder au
compte numéro 3, on peut faire un seek (ou
un fseek, selon la librairie qu'on utilise) pour se positionner sur le
3x8=24è octet du fichier et ainsi lire directement la ligne
correspondant au compte numéro 3. Il reste à lire les 8 octets correspondant au double.
---endCorr
Dans les questions suivantes, on décide de privilégier l'implémentation
3 qui minimise le nombre d'octets et le nombre de lectures de fichier
requises. Vous pouvez réaliser cette implémentation soit avec les
fonctions système d'accès aux fichiers (read, write...), soit avec les
fonctions de la librairie C (fread, fwrite..). Mais dans le cas des
fonctions de la librairie C, veillez à inhiber le tampon d'écriture des
fichiers (avec setvbuf) sous peine d'avoir des anomalies de
fonctionnement.
Question 1
Compléter le fichier banque.c de manière à ce
qu'il implémente l'algorithme suivant :
Lecture des
paramètres d'appel du programme
Ouverture fichier "banque.dat"
Lecture du solde stocké dans le fichier
Affichage de ce solde
sleep(5)
si (typeOp == 'L') alors
Lecture du solde stocké dans le fichier
Affichage du solde
sinon
Calcul
du
nouveau solde
Ecriture de ce nouveau solde dans le fichier
finsi
Fermeture du fichier
NB : si le fichier "banque.dat" n'existe pas, il
faut le
créer en l'initialisant avec NBCOMPTES valeurs de type double,
chacune étant initialisées à 0 et correspondant au
solde de chacun des comptes gérés par la banque.
Vérifier le
fonctionnement correct de l'application.
---beginCorr
Barême :
- 1 point pour l'ouverture "banque.dat"
- 1 point pour la création de "banque.dat" s'il n'existe pas.
- 1 point (0,5 pour le lseek et 0,5 pour le read) pour la lecture du solde.
- 1 point (0,5 pour le lseek et 0,5 pour le write) pour l'écriture du solde.
banque.corrige.c
---endCorr
Question 2
Ouvrir une deuxième fenêtre et placez-vous
également dans le répertoire Banque de votre
répertoire de travail
Taper "banque 0 L" ("L" comme "Lecture") dans la première
fenêtre (sans taper return)
Taper "banque 0 M -5" ("M" comme "Modification" ou "Mise-à-jour"
dans la deuxième fenêtre et appuyer sur return
Revenir dans la première fenêtre et appuyer sur return
Pourquoi "banque 0 L" affiche 2 soldes différents au cours de
son exécution ? Ce comportement est-il acceptable par un client
? Pour ces 2 questions, vous veillerez à répondre dans le
fichier reponses.txt.
---beginCorr
"banque 0 M -5" affiche "Solde initial du compte 0
= 0.00"
"banque 0 L" affiche "Solde initial du compte 0
= 0.00",
puis (après 5
secondes) "Solde du compte 0
=
5.00". En effet, pendant son sleep(5), "banque 0 M -5" finit son
exécution et donc modifie le solde du compte. La deuxième
lecture de "banque 0 L" est donc différente.
Le client devrait accepter ce léger dysfonctionnement (il ne lui
porte pas préjudice), même si ça peut le troubler. Barême :
- 1 point pour l'explication ?
- 0,5 pour l'explication de l'acceptabilité.
---endCorr
Question 3
Taper "banque 0 M -10" dans la première fenêtre (sans
taper return)
Taper "banque 0 M -5" dans la deuxième fenêtre et appuyer
sur return
Revenir dans la première fenêtre et appuyer sur return
Attendre la fin de l'exécution de ces 2 programmes, puis
exécuter "banque 0 L".
Commenter le résultat. Ce comportement est-il acceptable par un
client ? Vous veillerez à répondre dans le fichier reponses.txt.
---beginCorr
Le premier "banque 0 M -5" affiche "Solde initial du compte 0
= 5.00". Le
second aussi. "banque 0
L" affiche "Solde initial du compte 0
= 15.00",
puis (après 5
secondes) "Solde du compte 0
=
15.00".
L'écriture de "banque 0 M -5" a été
écrasée par l'écriture de "banque 0 M -10" ! On a
donc perdu une écriture, ce que le client n'acceptera pas
(surtout quand il s'agit d'un crédit sur son compte ;-) ). Barême :
- 1 point pour le commentaire
- 0,5 point pour l'explication de l'acceptabilité
---endCorr
Question 4
On souhaite corriger les dysfonctionnements observés aux
questions 2 et 3. Pour ce faire, on souhaite tester une solution
consistant en la pose du verrou le plus adapté aux
circonstances. L'algorithme envisagé est le suivant :
Lecture
des
paramètres d'appel du programme
Ouverture
fichier
"banque.dat"
Pose verrou en lecture // Nouveau par rapport a la question 1
Lecture du solde stocké dans le fichier
Affichage de ce solde
sleep(5)
si (typeOp == 'L') alors
Lecture du solde stocké dans le fichier
Affichage du solde
sinon
Pose verrou en écriture // Nouveau par rapport a la question 1
Calcul du
nouveau solde
Ecriture de ce nouveau solde dans le fichier
finsi
Relâchement verrou //
Nouveau par rapport a la
question 1
Fermeture du
fichier
Faire la commande cp banque.c banqueQ4.c
Implémenter l'algorithme ci-dessus dans la banqueQ4.c
Vérifier que les manipulations de la question 2
n'entraînent plus de dysfonctionnement.
Constater qu'en revanche, les manipulations de la question 3 donnent
lieu à une erreur. Expliquer, dans le fichier reponses.txt, le message d'erreur.
---beginCorr
Barême
- 1 point pour la pose de verrou en lecture
- 1 point pour la pose de verrou en écriture
- 1 point pour le relâchement de verrou
- 2 points pour l'explication du message d'erreur
banqueQ4.corrige.c
contient le
corrigé.
Lors des manipulations de la question 3, on a les messages suivants
L'un des processus
fait
tentative de pose de verrou
(partage) par processus
11214...
...Verrou (partage) pose par
11214
Solde initial du compte 0
=
-200.00
tentative de pose de verrou
(exclusif) par processus
11214...
... Verrou (exclusif) pose
par processus
11214
Relachement verrou par
processus
11214...
...OK
tandis que l'autre
fait:
tentative de pose de verrou
(partage) par processus
11215...
...Verrou (partage) pose par
11215
Solde initial du compte 0
=
-200.00
tentative de pose de verrou
(exclusif) par processus
11215...
Pose verrou: Resource
deadlock
avoided
Quand le premier processus essaye de poser son verrou en
écriture, Unix constate qu'il faut que ce processus attende que
le deuxième processus ait relâché le verrou qu'il
détient pour l'instant en lecture. Donc, il met le premier
processus en attente. Quand le deuxième processus essaye de
poser son verrou en écriture, Unix constate que ce processus
doit attendre que le premier processus ait relâché le
verrou qu'il a en lecture. Or, ce premier processus ne peut pas
relâcher ce verrou puisqu'il est déjà en attente
d'un relâchement de la part du deuxième processus. Par
conséquent, si jamais Unix met en attente le deuxième
processus, il y aura "verrou mortel" (les deux processus s'attendront
mutuellement jusqu'à la fin des temps). Pour empêcher
ça, Unix signale un message d'erreur pour cette demande de pose
de verrou.
Le deuxième processus arrête donc son exécution. De
ce fait, il relâche automatiquement son verrou en lecture. Le
premier processus peut donc acquérir son verrou en
écriture et continuer son exécution.
---endCorr
Question 5
On souhaite corriger le dysfonctionnement observé à la
question 4. Une personne propose l'algorithme suivant :
Lecture
des
paramètres d'appel du programme
Ouverture
fichier
"banque.dat"
Pose verrou en lecture
Lecture du solde stocké dans le fichier
Affichage de ce solde
sleep(5)
si (typeOp == 'L') alors
Lecture du solde stocké dans le fichier
Affichage du solde
sinon
Relâchement verrou //
Nouveau par rapport a la question 4
Pose verrou en
écriture
Calcul du nouveau solde
Ecriture de ce nouveau solde dans le fichier
finsi
Relâchement verrou
Fermeture du
fichier
Indiquez dans le fichier reponses.txt ce que vous pensez de cette proposition. NB : il n'est pas demandé
d'écrire un programme banqueQ5.c, même si ça peut
aider.
---beginCorr
Barême : 2 points pour le commentaire Le dysfonctionnement de la question 4 est effectivement corrigé.
Mais on retrouve le dysfonctionnement de la question 3 ! En effet,
quand le deuxième processus relâche son verrou avant de
chercher à poser son verrou en écriture, il
décoince le premier processus. Ce dernier est donc en mesure
d'écrire son nouveau solde. Donc, quand le deuxième
processus acquiert le verrou en écriture, il calcule le solde en
partant de l'ancienne valeur du solde. Le solde écrit par le
premier processus est perdu !
---endCorr
Question 6
Pour corriger tous les dysfonctionnements, une personne propose
l'algorithme suivant (consistant à poser dès le
début le verrou le plus fort dont on aura besoin) :
Lecture
des
paramètres d'appel du programme
Ouverture
fichier
"banque.dat"
si
(typeOp == 'L')
alors // Nouveau par rapport a la
question 4
Pose
verrou en lecture // Nouveau par rapport a la
question 4
sinon
// Nouveau par rapport a la
question 4
Pose
verrou en
écriture// Nouveau par rapport a la
question 4
finsi
//
Nouveau par
rapport a la question 4
Lecture du solde stocké dans le fichier
Affichage de ce solde
sleep(5)
si (typeOp == 'L') alors
Lecture du solde stocké dans le fichier
Affichage du solde
sinon
// Nouveau par rapport a la
question 4
(plus de "Pose
verrou en
écriture")
Calcul du nouveau solde
Ecriture de ce nouveau solde dans le fichier
finsi
Relâchement verrou
Fermeture du
fichier
Faire la commande cp banque.c banqueQ6.c
Implémenter l'algorithme ci-dessus dans la banqueQ6.c
Vérifier qu'il n'y a plus de dysfonctionnement.
---beginCorr
Barême : 2 points pour l'implémentation correcte de l'ensemble
banqueQ6.corrige.c
contient le
corrigé.
---endCorr
Question 7
banqueQ7 a en fait encore un dysfonctionnement !
Ouvrir une troisième fenêtre :
- Taper "banque 0 M -10" dans la première
fenêtre (sans taper return)
- Taper "banque 0 L" dans la deuxième fenêtre
(sans taper return)
- Taper "banque 0 L" dans la troisième fenêtre
et appuyez sur return
- Aller dans la première fenêtre et appuyer sur
return
- Aller dans la deuxième fenêtre et appuyer sur
return
- Dès que "banque 0 L" a fini dans la troisième
fenêtre, relancer le.
- Dès que "banque 0 L" a fini dans la deuxième
fenêtre, relancer le.
- On observe que "banque 0 M -10" n'arrive jamais à
s'exécuter !
Ce dysfonctionnement ne pourra être corrigé que lors de
l'exercice CommInterProcessus/7
---beginCorr
Le phénomène observé est une famine de
rédacteur face à de nombreux lecteurs (cf. chapitre
"Synchronisation des processus").
---endCorr
|