Série n 1 / Solutions Exercice n 1 - les deux processus partagent une variable booléenne interesse qui sera vraie si le processus est en section critique ou demande à y rentrer, et fausse sinon, /* contexte commun */ enum BOOLEEN FAUX,VRAI; enum BOOLEEN interesse = FAUX; void entrer_section_critique(void) while (interesse == VRAI); interesse = VRAI; void sortir_section_critique(void) interesse = FAUX; supposons que interesse soit à la valeur FAUX, si chaque processus teste la variable avant que l'autre ne lui ait affecté la valeur VRAI, les deux processus s'engagerons en section critique. - les deux processus partagent une variable entière tour dont la valeur est égale au numéro du processus autorisé à s'engager en section critique, /* contexte commun */ int tour = 0; while (tour == autre); void sortir_section_critique(int processus) tour = 1 - processus; Le processus i ne peut entrer en section critique que si tour vaut i, donc l'exclusion mutuelle est assurée puisque l'accès à une variable est garanti par l'indivisibilité de
l'opération. Cependant, cette solution impose un fonctionnement en bascule de deux processus, ce qui est contraire avec la propriété d'indépendance (si un processus s'arrête après l'exécution de la fonction sortir_section_critique alors l'autre ne peut y rentre qu'une seule fois). - à chaque processus est associée une variable booléenne qui sera vraie si le processus est en section critique ou demande à y rentrer, et fausse sinon. /* contexte commun */ enum BOOLEEN FAUX,VRAI; enum BOOLEEN interesse[2] = FAUX,FAUX; while (interesse[autre] == VRAI); interesse[processus] = VRAI; void sortir_section_critique(int processus) interesse[processus] = FAUX; A la suite de la séquence suivante : - le processus 0 consulte interesse[1] est le trouve FAUX, - le processus 1 consulte interesse[0] est le trouve FAUX, - le processus 0 met interesse[0] à VRAI et entre en section critique, - le processus 1 met interesse[1] à VRAI et entre en section critique, l'exclusion mutuelle n'est donc pas assurée. On peut essayer de corriger la situation en faisant l'affectation avant le test : interesse[processus] = VRAI; while(interesse[autre] == VRAI); L'exclusion mutuelle est garantie : en effet, si le processus i trouve interesse[j] à FAUX, il entre en section critique, et si plus tard le processus j souhaite rentrer en section critique, il trouvera interesse[i] à VRAI et attendra. Toutefois, à la suite de la séquence suivante :
- le processus 0 met interesse[0] à VRAI, - le processus 1 met interesse[1] à VRAI, - le processus 1 consulte interesse[0] est le trouve VRAI, il boucle, - le processus 0 consulte interesse[1] est le trouve VRAI, il boucle, les deux processus rentre en boucle infinie, ce qui est contraire à la deuxième règle. Exercice n 2 enum BOOLEEN NON,OUI; int tour, interesse[2]; interesse[processus] = OUI; /* je veux rentrer */ tour = num_processus; /* c'est mon tour */ while (tour == processus && interesse(autre) == OUI); void sortir_section_critique(int processus) interesse[processus] = NON; Le programme ci-dessus a été imaginé par Peterson en 1980 (suite aux travaux de Dekker et Dijkstra en 1965). Le principe est simple : chaque processus doit avant d'utiliser les variables partagées, appeler entrer_section_critique, et lorsqu'il finit de les utiliser appeler sortir_section_critique. Au départ, aucun processus n'est en section critique. Le processus 0 appelle entrer_section_critique, indique qu'il est intéressé en mettant sa position à OUI, positionne tour à 0, et la fonction se termine. Si le processus 1 appelait entrer_section_critique, il devrait attendre jusqu'à ce que interesse[o] passe à FAUX, événement qui ne se produira que lorsque le processus 0 appellera sortir_section_critique. Considérons la situation où les deux processus appellent entrer_section_critique presque simultanément. Les deux sauvegarderons leur numéro dans tour, la dernière valeur sauvée efface donc la première. Si le processus 1 enregistre son numéro en dernier, lorsque les deux processus arrive sur la boucle while, le processus 0 rentre en section critique tandis que le processus 1 boucle.
Exercice n 3 - priorité des lecteurs sur les rédacteurs : le seul cas d'attente d'un lecteur se produit quand un rédacteur écrit dans le fichier ; un rédacteur ne peut accéder au fichier que si aucun lecteur n'est en attente ou en cours de lecture. int nl = 0; semaphore mutex1 = 1, mutex2 = 1, w = 1; LECTEURS P(mutex1) nl = nl + 1; si (nl == 1) P(W); V(mutex1) lecture du fichier P(mutex1) nl = nl - 1; si (nl == 0) ; V(mutex1) REDACTEURS P(mutex2) P(W) ecrire dans le fichier V(mutex2) w est un sémaphore d'exclusion mutuelle pour tous les rédacteurs. Il sert également au premier lecteur qui occupe le fichier et au dernier qui le libère. mutex1 est un sémaphore d'exclusion mutuelle pour les lecteurs uniquement, il protège la variable nl. Si un lecteur est bloqué par w, tous les autres le seront par mutex1. mutex2 est un sémaphore d'exclusion mutuelle pour les rédacteurs uniquement. Il assure la priorité des lecteurs sur les rédacteurs car il empêche plusieurs rédacteurs de se bloquer sur w. - priorité des lecteurs sur les rédacteurs si et seulement si un lecteur occupe déjà le fichier : les lecteurs et les rédacteurs ont la même priorité quand aucun lecteur ne lit ; dès qu'un lecteur lit, tous les autres lecteurs peuvent lire et ce quelque soit le nombre de rédacteurs en attente. int nl = 0; semaphore mutex = 1, w = 1; LECTEURS nl = nl + 1; si (nl == 1) P(W); lecture du fichier nl = nl - 1; si (nl == 0) ; REDACTEURS P(W) ecrire dans le fichier
w est un sémaphore d'exclusion mutuelle pour tous les rédacteurs. Il sert également au premier lecteur qui occupe le fichier et au dernier qui le libère. mutex est un sémaphore d'exclusion mutuelle pour les lecteurs uniquement, il protège la variable nl. Si un lecteur est bloqué par w, tous les autres le seront par mutex. - les lecteurs et les rédacteurs ont la même priorité : si un lecteur utilise le fichier, alors les nouveaux lecteurs y accèdent également et ce jusqu'à l'arrivée d'un rédacteur, à ce moment là les nouveaux arrivants attendent sans distinction de catégorie ; de même si un rédacteur utilise le fichier, les nouveaux arrivants attendent, et quand le rédacteur aura fini, il réveillera le premier processus en attente ; si plusieurs lecteurs se suivent dans une file d'attente, ils accèdent ensemble au fichier. int nl = 0; semaphore mutex = 1, r = 1, w = 1; LECTEURS P(r) nl = nl + 1; si (nl == 1) P(W); V(r) lecture du fichier REDACTEURS P(r) P(w) ecrire dans le fichier nl = nl - 1; si (nl == 0) ; V(r) V(r) Le sémaphores r bloque tous les lecteurs nouveaux dès qu'un rédacteur est arrivé. Le sémaphore w bloque toute écriture tant que les lectures ne sont pas finies ; il y a au plus un rédacteur bloqué dans la file associée à w. Dès qu'un lecteur passe ou est réveillé, il incrémente le compteur des lecteurs, bloque éventuellement la ressource et réveille le processus suivant de la file associée à r. L'ordre P(r) P(w) évite l'étreinte fatale ; on peut aussi faire V(r) puis V(w). L'ordre FIFO est assuré si les file associées aux sémaphores sont gérées suivant ce principe.
Exercice n 4 - le carrefour peut contenir une voiture semaphores mutex1 = 1, mutex2 = 1, feu1 = 1, feu2 = 0 Traversée 1 Changement Traversée 2 P(mutex1) a est un booleen VRAI; P(mutex2) P(feu1) boucle infinie P(feu2) traversée attendre(m) traversée V(feu1) si (a) alors V(feu2) V(mutex1) P(feu1) V(mutex2) V(feu2) a <- FAUX sinon P(feu2) V(feu1) a <- VRAI fin si fin boucle infinie feu1 et feu2 règle chaque file. Ils évitent aussi toute modification de feu tant qu'une voiture est dans le carrefour. mutex1 et mutex2 évite une coalition de voiture visant à bloquer un feu => une seule voiture bloqué sur feu1 ou feu2. - le carrefour peut contenir k voitures semaphores mutex1 = k, mutex2 = k, feu1 = 1, feu2 = 0, mutex = 1, w = 0 Traversée 1 Changement P(mutex1) a est un booleen VRAI; P(feu1) boucle infinie attendre(m) n <- n+1 si (a) si (n=1) P(W) P(feu1) P(W) V(feu1) V(feu2) traversée a <- FAUX sinon n <- n-1 P(feu2) si (n=0) P(W) P(W) V(feu21) V(mutex1) a <- VRAI fin si fin boucle w bloque le changement de feu tant qu'il y a des voitures dans le carrefour. A cause de P(feu) est P(w) seul le changement peut-être bloqué sur w. feu1 et feu2 règle chaque file. mutex1 et mutex2 laissent passer au plus k voitures.