Vous êtes sur la page 1sur 6

UFR FACULTÉ DES SCIENCES

CORRIGÉ
L36I – programmation système et réseaux
Corrigé du devoir surveillé
PREMIERE PARTIE

Question 1 ( 2 points ≃ 12 mn )

T1: c=13
T2: k=3 /* c’est ce qu’on constate toujours, mais toute autre valeur était acceptable */
T3: p=8
T4: b=cou

Question 2 ( 3 points ≃ 18 mn )

T1: b=mes
T2: b=sag
T3: b=e c c=1

Question 3 ( 3 points ≃ 18 mn )

T1: b=mes
T2: 1 <>
Question 4 ( 4 points ≃ 24 mn )

Numérotons les instructions importantes :


int main(void){
01 strcpy(x, "aaaaa");
if(!t){ // père -----------------------------
buf.mtype = 100;
02 strcpy(buf.mtext,"bbbbb");
03 if((int)(x= (char*) shmat(shmid1, 0, 0))<0) ERRNO_EXIT;
04 sleep(1);
05 strcpy(x, "ccccc");
06 if(msgsnd(msgid1, &buf, sizeof(buf), 0) <0) ERRNO_EXIT;
07 printf("T1: x=%s\n", x);
08 exit(1);
} else { // fils -------------------------------
09 if((int)(x= (char*) shmat(shmid1, 0, 0)) <0) ERRNO_EXIT;
10 strcpy(x,"ddddd");
11 if(msgrcv(msgid1, &buf, sizeof(buf), 0, 0) <0) ERRNO_EXIT;
12 printf("T2: x=%s\n",x);
13 strcpy(x,buf.mtext);
14 printf("T3: x=%s\n",x);
}
La seule synchronisation (après le fork) entre les processus père et fils est la communication par message. La
recopie de la chaîne "aaaaa" dans x est écrasée par les attachements de segments effectués par le père et le fils en
respectivement lignes 03 et 08 avant les impressions. Le sleep(1) donne une très grande probabilité que le contrôle
soit passé au fils à partir de 04, donc que 09:10 soient exécutés avant 05:06, mais ce n’est pas certain si la machine
est très chargée. Comme le père envoie à son fils un message en 06, cela réalise un rendez-vous entre 06 et 11, car
les lectures de messages sont blocantes. Donc le fils ne peut pas imprimer la trace T2 avant que son père ait envoyé
le message sur la file : 12:14 est forcément après 02:06. On a donc l’ordonnancement suivant :
1) x:= "aaaaa"
2) en parallèle, ordre indéterminé, {02:04} // {09 x:= "ddddd" ; 10}
3) probablement (sleep), mais pas sûr, {05 x:= "ccccc" ; 06}. Mais 05:06 peut aussi, mais moins pro-
bablement, être en parallèle avec 09:10. Donc, à la fin de cette étape 3, x vaut probablement "ccccc",
certainement pas "aaaaa", mais peut-être "ddddd" ou un entrelacement de "c" et de "d" comme
"ccddccdddc".
4) forcément 11 est après 06, et 07 est après 06. Donc on a en parallèle
{07 print T1, x} // {11;12 print T2,x;13 x:="bbbbb";14 print T3,x}
L’exécution est donc très aléatoire. D’une part, les écritures dans la chaîne x peuvent se faire dans un ordre
arbitraire, voire être entrelacées et d’autre part les impressions de cette chaîne sont elles-même dans un ordre non
garanti.
Les impressions possibles sont donc:
a)
T1: ccccc (probable) ou ddddd ou entrelacement arbitraire de "c" et "d"
T2: même valeur que ci-dessus
T3: bbbbb
ou b)
T2: ccccc (probable) ou ddddd ou entrelacement arbitraire de "c" et "d"
T1: même valeur que ci-dessus
T3: bbbbb
ou c)
T2: ccccc (probable) ou ddddd ou entrelacement arbitraire de "c" et "d"
T3: bbbbb
T1: bbbbb
Pour avoir une exécution décidable, il faudrait établir une synchronisation plus précise entre les processus père et
fils, car d’une part l’écriture dans le segment de mémoire partagée est une section critique non protégée par un
sémaphore et l’ordonnancement des impressions {T1} // {T2;T3} est lui-même non synchronisé, dans un ordre
arbitraire.
DEUXIEME PARTIE

Question 5 ( 2 points ≃ 12 mn )

Question 5.1 ( 1 point ≃ 6 mn )

Les deux processus veulent acquérir deux ressources par deux opérations distinctes non atomiques. Il se peut que :
P1 demande R1 et l’obtienne (P1:03)
P2 demande R2 et l’obtienne (P2:03)
P1 demande R2 et reste bloqué (P1:04)
P2 demande R1 et reste bloqué (P2:04)

Question 5.2 ( 1 point ≃ 6 mn )

Il faut rendre indivisible l’acquisition des deux sémaphores pour éviter que les processus commencent à aquérir des
ressources sans les obtenir toutes et rester mutuellement bloqués. Cela peut être réalisé, soit avec un sémaphore
multiple (comme dans les IPC système V), soit par un sémaphore d’exclusion mutuelle initialisé à 1. On peut
remarquer aussi que si l’un des processus acquiert les deux ressources, il ne les relâche toutes les deux qu’après la
ligne 08. Donc les séquences 03:08 sont en exclusion mutuelle, et un seul sémaphore mutex suffirait !!! Toutes les
solutions avec un sémaphore mutex qui rend critique les séquences 03 et 04 au minimum et jusqu’à 08 sont accep-
tables. Certains étudiants ont utilisé un sémaphore de synchronisation initialisé à 0 pour empêcher que les lignes
03 s’exécutent l’une derrière l’autre entre les deux processus. Cela résoud le problème de l’inter-blocage, mais ne
permet pas à un processus de faire plusieurs tours pendant que l’autre en fait moins. C’est une synchronisation
excessive, car si à la ligne 09 chaque processus réalise un travail T plus ou moins long, cela oblige le plus rapide à
attendre l’autre, alors qu’il pourrait faire plusieurs tours complets pendant que l’autre réalise ce travail T.

Déclarations communes : Sémaphore R1 INIT(1);


Sémaphore R2 INIT(1);
Sémaphore MUTEX INIT(1);

01 Processus P1 01 Processus P2
02 répéter 02 répéter
02b P(MUTEX,1) 02b P(MUTEX,1)
03 P(R1) 03 P(R2)
04 P(R2) 04 P(R1)
04b V(MUTEX,1) 04b V(MUTEX,1)
05 <Utiliser R1 et R2> 05 <Utiliser R1 et R2>
06 V(R1) 06 V(R2)
07 <Utiliser seulement R2> 07 <Utiliser seulement R1>
08 V(R2) 08 V(R1)
09 <travail T utilisant ni R1 ni R2 > 09 <travail T utilisant ni R1 ni R2 >
10 éternellement 10 éternellement

Question 6 ( 6 points ≃ 36 mn )

Question 6.1 ( 2 points ≃ 12 mn )

Le diagramme de séquences suivant indique les synchronisations nécessaires pour les différentes transitions
des feux (les feux dans une même direction N et S ou O et E sont identiques) :
NS: répéter R <sync1> R <sync2> V <sync3> O <sync4> éternellement
OE: répéter V <sync1> O <sync2> R <sync3> R <sync4> éternellement
Une solution avec 4 sémaphores de synchronisation marche donc. Mais on remarque que les synchronisations
aux points <sync1> et <sync3> ne sont pas nécessaires, car ce qui importe c’est que les feux gérés par l’un des
processus restent au rouge pendant que ceux de la direction orthogonale restent au vert, puis passent à l’orange. Il
n’est pas nécessaire de synchroniser la transition vert → orange avec un instant précis pendant que l’autre direction
reste au rouge ! On peut donc simplifier le diagramme de séquences :
NS: répéter R <sync2> V O <sync4> éternellement
OE: répéter V O
<sync2> R <sync4> éternellement
Mais une seule synchronisation ne suffit pas: il faut que que les transitions R → V d’une direction se fasse en
même temps que O → R de l’autre direction. Donc deux sémaphores distincts de synchronisation sont nécessaires
pour le passage au vert de NS et de OE, appelés VNS et VOE. Il faut cependant faire attention à choisir quel
processus démarre en premier, pour éviter un interblocage : par exemple en initialisant un sémaphore à 1 et l’autre
à 0.
Mais pour une synchronisation, équitable, on peut tout aussi bien permuter les P et V, c’est-à-dire de demander
l’autorisation de passer au rouge. Et même panacher pour un même processus, demander l’autorisation de passer
au rouge et au vert... Il y a donc quatre solutions possibles.
Remarques.
1) Certains étudiants ont bien utilisé deux sémaphores, mais ils ont écrit la primitive V(VXX,1) juste avant la fin
de boucle. Cela n’est pas totalement correct, car cela ne garantit pas que la transition "passer au rouge" se fasse
en même temps que "allumer le vert" de l’autre direction: il se pourrait que le rouge soit allumé depuis quelque
temps lorsque l’autre direction passe au vert (ce qui ne serait pas très grave).
2) De même, ceux qui ont utilisé un seul sémaphore d’exclusion mutuelle ont tort: cela ne garantit pas totalement
l’alternance des cycles des deux directions, une direction pourrait faire plusieurs cycles pendant que l’autre
musarde, même si cela est improbable.
3) Juste après l’exercice sur l’interblocage, quelques étudiants commencent leur synchronisation par un interblo-
cage ou font démarrer les feux verts en même temps : leur père est carrossier ?
4) certains étudiants n’hésitent pas à tester la valeur d’un sémaphore pour ensuite lui appliquer une opération de
transition P ou V ⇒ ils n’ont toujours pas compris l’importance de l’indivisibilité de certaines sections de code,
ni l’utilité des sémaphores !

Variables et déclarations communes:


sémaphore VNS init 1; /* demande d’autorisation de passer au vert pour NS*/
sémaphore VOE init 0; /* demande d’autorisation de passer au vert pour OE*/

Processus NS Processus OE
répéter répéter
P(NS,1) P(OE,1)
<Eteindre rouge et Allumer vert> <Eteindre rouge et Allumer vert>
<Rester vert 45 sec> <Rester vert 45 sec>
<Eteindre vert et Allumer orange> <Eteindre vert et Allumer orange>
<Rester orange 4 sec> <Rester orange 4 sec>
V(OE,1) V(NS,1)
<Eteindre orange et allumer rouge> <Eteindre orange et allumer rouge>
éternellement éternellement

Question 6.2 ( 2 points ≃ 12 mn )

Comme précédemment, les transitions "vert → orange" ne nécessitent pas de synchronisation. Ce qui im-
porte ce sont les transitions "orange → rouge" ("passage au rouge") et "rouge → vert" ("passage au vert"). Mais
maintenant, il faut que 3 processus soient arrivés au point de passage au rouge pour que le 4e passe au vert.
Une première solution est d’utiliser un sémaphore avec 3 jetons au point de passage d’un processus au vert.
Il suffit alors de faire P(sem,3) (demande de 3 autorisations) et les trois autres processus feront V(sem,1) au
point où ils "passent au rouge" pour les trois autres.
Une deuxième solution, plus simple, est de rendre chaque feu responsable des autorisations du feu suivant
dans l’ordre antihoraire N, O, S, E, avec quatre sémaphores de synchronisation, bloqués au départ sauf un chois
arbitrairement, par exemple N. C’est la solution que nous choisissons :

Variables et déclarations communes:


sémaphore N init 1; /* demande d’autorisation de passer au vert pour le feu N*/
sémaphore E init 0; /* demande d’autorisation de passer au vert pour le feu E*/
sémaphore S init 0; /* demande d’autorisation de passer au vert pour le feu S*/
sémaphore O init 0; /* demande d’autorisation de passer au vert pour le feu O*/

Processus Nord Processus Ouest


répéter répéter
P(N,1); P(O,1);
<Eteindre rouge et Allumer vert> <Eteindre rouge et Allumer vert>
<Rester vert 45 sec> <Rester vert 45 sec>
<Eteindre vert et Allumer orange> <Eteindre vert et Allumer orange>
<Rester orange 4 sec> <Rester orange 4 sec>
V(O,1); V(S,1);
<Eteindre orange et allumer rouge> <Eteindre orange et allumer rouge>
éternellement éternellement

Question 6.3 ( 2 points ≃ 12 mn )

On a les diagrammes de séquences suivants. Les mots sn indiquent un point de synchronisation situé juste
avant la colonne de transition où il apparait. Ainsi s1 est le point de synchronisation de l’allumage de la flêche de
Nord, pendant que le Rouge reste allumé, que Ouest reste au rouge, que Sud passe de l’orange au rouge et que Est
passe du rouge au vert.
s1 s2 s3 s4 s5 s6 s7 s8
Nord répéter R+F R V O R R R R éternellement
Ouest répéter R R R+F R V O R R éternellement
Sud répéter R R R R R+F R V O éternellement
Est répéter V O R R R R R+F R éternellement
On remarque que chaque direction connait cinq transitions :
• t1: R → R+F,
• t2: R+F → R
• t3: R → V,
• t4: V → O,
• t5: O → R

qui sont commandées par les changements d’états des autres feux. Une solution simpliste est donc d’utiliser 5
sémaphores par direction, soit 20 au total, N1, N2, N3, N4, N5, O1, O2, etc. Tous ces sémaphores seraient
initialisés à 0 sauf N1 à 1 pour démarrer. Les transitions d’un feu sont alors commandées par les autres feux, dont
les trois premières par le feu précédent.
On peut heureusement faire plus simple, en partant de la solution 6.2 qui se contentait d’un seul sémaphore par
direction, à laquelle on ajoute 2 sémaphores pour allumer et éteindre la flêche, soit 3 sémaphores par feu. Avec
cette solution, les 3 premières transitions peuvent être directement commandées par le feu précédent dans le sens
antihoraire. Les transitions t4 et t5 n’ont pas besoin d’être synchronisées avec les transitions des autres feux.
Et on peut même se contenter de 4 sémaphores de synchronisation, un par direction, chaque sémaphore com-
mandant les 3 premières transitions, les seules pertinentes. Mais dans ce cas, le problème est de les faire démarrer
correctement en effectuant des parmutations circulaires et en se fiant au tableau général des synchronisations.
Il existe d’autres solutions plus ou moins compliquées, mais peu de copies en ont proposé qui marchent bien.
Exemple de solution avec 4 sémaphores et permutations circulaires :
sémaphore N init 1; /* t1, t2, t3 pour Nord */
sémaphore O, S, E init 0; /* t1, t2, t3 pour O, S, E */

Processus Nord Processus Ouest


répéter répéter
P(N,1); /* s3, t3N*/ P(O,1); /* s3, t1O*/
V(O,1); /* s3, t1O*/ <Allumer flèche à droite clignotante>
<Eteindre rouge et Allumer vert> P(O,1); /* s4, t2O*/
<Rester vert 45 sec> <Eteindre flèche à droite clignotante>
/* s4, t4N*/ P(O,1); /* s5, t3O*/
V(O,1); /* s4, t2O*/ V(S,1); /* s5, t1S*/
<Eteindre vert et Allumer orange> <Eteindre rouge et Allumer vert>
<Rester orange 4 sec> <Rester vert 45 sec>
/* s5, t5N*/ /* s6, t4O*/
V(O,1); /* s5, t3O*/ V(S,1); /* s6, t2S*/
<Eteindre orange et allumer rouge> <Eteindre vert et Allumer orange>
P(N,1); /* s1, t1N*/ <Rester orange 4 sec>
<Allumer flèche à droite clignotante> /* s7, t5O*/
P(N,1); /* s2, t2N*/ V(S,1); /* s7, t3S*/
<Eteindre flèche à droite clignotante> <Eteindre orange et allumer rouge>
éternellement éternellement

Une autre variante est d’utiliser le même sémaphore pour commander simultanément le passage au vert d’un
feu et l’allumage de la flêche du feu suivant dans le sens antihoraire. Mais dans ce cas, il ne faut pas que tous les
processus démarrent au point de synchro t3, mais les faire démarrer au point de synchro s3 (par exemple).

Variables et déclarations communes:


sémaphore NEF init 0; /* t2: R+F->R, Eteindre Flêche pour Nord */
sémaphore NAV init 2; /* t3: R->V, Allumer Vert pour Nord */
/* t1: R->R+F, Allumer Flêche pour Ouest */
sémaphore OEF, SEF, EEF init 0; /* t2: R+F -> R pour O, S, E */
sémaphore OAV, SAV, EAV init 0; /* t3: R -> V pour O, S, E */
/* t1: R -> R+F pour S, E, N */

Processus Nord Processus Ouest


répéter répéter
P(NAV,1); /* s3, t3N*/ P(NAV,1); /* s3, t1O*/
<Eteindre rouge et Allumer vert> <Allumer flèche à droite clignotante>
<Rester vert 45 sec> P(OEF,1); /* s4, t2O*/
/* s4, t4N*/ <Eteindre flèche à droite clignotante>
V(OEF,1); /* s4, t2O*/ P(OAV,1); /* s5, t3O*/
<Eteindre vert et Allumer orange> <Eteindre rouge et Allumer vert>
<Rester orange 4 sec> <Rester vert 45 sec>
/* s5, t5N*/ /* s6, t4O*/
V(OAV,2); /* s5, t3O*/ V(SEF,1); /* s6, t2S*/
<Eteindre orange et allumer rouge> <Eteindre vert et Allumer orange>
P(NAF,1); /* s1, t1N*/ <Rester orange 4 sec>
<Allumer flèche à droite clignotante> /* s7, t5O*/
P(NEF,1); /* s2, t2N*/ V(SAV,2); /* s7, t3S*/
<Eteindre flèche à droite clignotante> <Eteindre orange et allumer rouge>
éternellement éternellement

Vous aimerez peut-être aussi