Corrigé de l'examen de systèmes I (licence - Janvier 2001)
La séquence montre qu'il est possible d'obtenir la liste des références
contenues dans le répertoire rep (droit de lecture
sur le répertoire). La seconde commande tente d'obtenir des informations
descriptives sur les références contenues. Pour cela elle doit effectuer un
appel à stat() en utilisant
la référence de chacun des objets, rep/f1 et rep/f2. La présence d'un répertoire dans un chemin d'accès
n'est autorisée qu'à condition que le droit de le traverser soit présent
(droit d'exécution). Par conséquent on peut affirmer que le répertoire est
ouvert en lecture mais pas en exécution pour l'utilisateur considéré.
L'exécutable a (dans le cas où aucune erreur
n'est produite) a pour effet d'ouvrir en écriture le fichier f tout en le vidant de son contenu, puis d'y écrire 100000
exemplaires du premier caractère de la commande.
- La commande permet d'obtenir un nouveau lien b
sur l'inoeud référencé par a.
- La commande exécute en concurrence deux exemplaires de la commande
a. Quel que soit l'ordonnancement choisi
100000 caractères a seront placés dans le
fichier préalablement vidé. Évidemment la dernière commande à exécuter
l'appel à open()
aura effacé les caractères déjà écrits par l'autre, mais comme elles
écrivent la même chose pour finir rien ne sera visible. De plus même
si la dernière opération de troncature crée un fichier à trous
(puisque qu'une autre commande continue d'écrire dans le fichier avec
une position déjà avancée), les trous seront tous bouchés grâce aux
écritures séquentielles.
- Cette commande exécute en concurrence la commande a chargée d'écrire 100000 a et la commande b
chargée d'écrire 100000 b. Le contenu de
f sera probablement un mélange de caractères
a et b pour un total
de 100000. En effet il est possible (et fortement probable) que les
processus se doublent. On aura par exemple l'exécution suivante :
- a appelle open() et vide le fichier
- a appelle write() et écrit un a en première position (le fichier contient
a)
- a appelle write() et écrit un a en seconde position (le fichier contient
aa)
- b appelle open() et vide le fichier (le
fichier est vide)
- a appelle write() et écrit un a en troisième position (le fichier contient
<NUL><NUL>a)
- b appelle write() et écrit un b en première position (le fichier contient
b<NUL>a)
- b appelle write() et écrit un b en deuxième position (le fichier contient
bba)
- b appelle write() et écrit un b en troisième position (le fichier contient
bbb)
- b appelle write() et écrit un b en quatrième position (le fichier contient
bbbb)
- a appelle write() et écrit un a en quatrième position (le fichier contient
bbba)
- etc.
Dans ce cas, quelle que soit la suite des événements le fichier
commencera par bbba.
- Maintenant il existe une nouvelle commande b
(différente de a) qui tronque le fichier f à l'ouverture, mais effectue des écritures en mode
ajout (écriture systématiquement réalisée en fin de fichier). Dans la
suite on notera Ti l'opération de
troncature réalisée par la commande i, et i l'opération d'écriture du caractère i par la commande i.
- Dans ce cas, il est possible que des caractères a écrasent des b (mais pas
l'inverse!). En effet, la trace d'exécution suivante est possible :
TaTbbbaaab... ce qui
donne pour la trace du contenu du fichier correspondant :
(vide),(vide),(b),(bb),(ab),(aa),(aaa),(aaab),...
Dans ce cas, on constate que des b ont
bien été recouverts par des a. L'inverse
n'est pas possible car les b sont toujours
écrits après la dernière position. On peut donc affirmer qu'au moins
100000 caractères seront présents dans le fichier (cas dans lequel
tous les b sont recouverts par des a, le fichier contiendrait alors "a100000") et qu'au plus 200000
caractères seront présents (cas dans lequel aucun b n'a été recouvert, le fichier contiendrait "a100000b100000").
Si aucun a ne peut être recouvert par un
b cela ne signifie pas pour autant que des
b ne puisse pas apparaître (dans le résultat)
avant des a, car il ne faut pas oublier que
des a écrits peuvent être effacés par
l'opération de troncature de la commande b.
On définit informellement de la façon suivante une trace correcte
possible :
- il n'apparaît pas d'opération i avant
Ti (on ne peut écrire avant
d'avoir ouvert le fichier)
- le nombre d'opération i est égal à
100000 (on réalise toutes les écritures)
Les traces peuvent donc être de deux sortes :
- TaaiTbbja...
- Deux cas se présentent :
- i<=j
- Les i premiers a sont effacés par l'opération Tb qui vide le fichier,
ensuite certains b (j>=i) sont écrits puis un a en position i+1 et enfin le reste (des a et des b
avec d'éventuels recouvrements). Dans ce cas on obtient un
fichier contenant "bia100000-ibk" avec k<10000-j (la différence 100000-k-j est le nombre de
recouvrements).
- j<=i
- Les i premiers a sont effacés par l'opération Tb qui vide le fichier,
ensuite certains b (j<=i) puis un a en position i+1 (laissant des caractères nuls entre
j+1 et i
compris), enfin le reste (des a
et des b avec d'éventuels
recouvrements). Dans ce cas on obtient un fichier
contenant "bj<nul>i-ja100000-ibk" avec k<100000-j (la différence est le
nombre de recouvrements).
- TbbiTa...
- Les i premiers b sont effacés par l'opération Ta, ensuite seront écrits des
a et des b
avec d'éventuels recouvrements. Dans ce cas on obtient un
fichier contenant "a100000bk" avec k<N-i (la différence étant le nombre de
recouvrements).
- Dans ce cas on obtiendra au moins 100000 et au plus 200000 b dans le fichier. Ici on peut remarquer qu'il n'y
a jamais de recouvrement, seuls certains caractères peuvent avoir été
effacés lors de l'opération de troncature.
- La commande affiche le nombre de caractère lus dans le fichier f par la commande c
laquelle est en concurrence avec la commande a qui construit le fichier f. S'il n'est pas possible de lire plus de 100000
caractères, il est envisageable d'en lire moins, pour cela il suffit
que la commande c atteigne la fin du fichier
avant que a n'ait terminé.
- Il est très difficile de dire exactement qu'elle sera son effet,
mais il est fort probable que plus sa valeur sera grande moins le
nombre de caractères lus dans le fichier sera important. L'effet
aurait été bien plus visible si la condition d'arrêt avait été (l=read(d,c,LG))==LG (on aurait supposé que si
moins de LG caractères sont lus alors il n'y en aura plus
ensuite!).
Tout d'abord il faut remarquer que le programme p copie caractère par
caractère le fichier donné en premier argument dans le second. Les ouvertures
sont effectuées dans l'ordre suivant : ouverture en lecture du premier puis en
écriture du second.
- Il n'y a normalement aucun problème. Le fichier f2 contient une copie des données du fichier f1.
- Ici, il faut remarquer que t est un tube nommé
sur lesquels les ouvertures en lecture et écriture sont synchronisées (en
mode bloquant). Donc la commande qui reçoit le tube en premier paramètre
et qui tente de l'ouvrir en lecture attendra que la commande qui le reçoit
en second paramètre tente de l'ouvrir en écriture. Ensuite la recopie se
produira par l'intermédiaire du tube (attente sur tube vide en lecture et
sur tube plein en écriture).
- Ici, t1 et t2 sont
des tubes nommés. p t1 t2 est bloquée en attente
de l'ouverture en écriture de t1 par quelqu'un
d'autre et p t2 t1 est bloquée en attente de
l'ouverture en écriture de t2 par quelqu'un
d'autre. Il y a donc une situation de blocage sur les ouvertures.
- Cette fois les ouvertures sont non bloquantes (en lecture pas de
problème, mais en écriture il y a échec si pas de lecteur).
Cela ne change rien à la première commande.
Pour la seconde une erreur peut se produire si la commande qui tente
d'ouvrir en écriture passe la première (pas de lecteur donc échec), alors
la seconde ouvrira correctement en lecture mais échouera lors de l'appel à
read(). Si les
ouvertures se passent dans le bon sens, il est aussi possible que
l'écrivain ne puisse pas assez vite ses données et que le lecteur se
trouve face à un tube vide conduisant à une lecture de fin de fichier.
Pour la troisième commande il n'y a plus d'interblocage à l'ouverture
mais des échec divers et variés peuvent se produire soit sur une ouverture
en écriture sans lecteur (terminaison), soit lecture sur tube vide (fin de
fichier). On peut noter que rien ne circulera dans les tubes puisqu'aucune
lecture n'aboutira...
L'auteur de cette page est : Jean-Baptiste Yunès


