Vous êtes sur la page 1sur 14

LES SEMAPHORES.

Les multiples threads d’exécutions d’une application doivent être capables :

 de synchroniser leur exécution


 de se coordonner pour des accès exclusif à des ressources partagées (shared ressources).
Pour réaliser ces synchronisations et ses partages, les noyaux RTOS offrent des objets sémaphore
ainsi que des services de gestions des sémaphores.

1. Définition.
Un sémaphore (semaphore ou semaphore token) est un objet du noyau d’un ou plusieurs
thread peut acquérir ou relâcher dans un but de synchronisation ou d’exclusion mutuelle.
Au début de la création d’un sémaphore, le noyau un bloc de contrôle (SCB pour Semaphore
Control Block), un unique ID, une valeur (binaire ou numérique) et une file d’attente des
taches (tasks waiting list).

Un sémaphore est un peu une clé qui permettra à une tache de réaliser certaines opérations ou
d’avoir accès à certaines ressources. Si la tache peut acquérir le sémaphore, elle pourra réaliser
les deux actions ci-dessus.
Un sémaphore unique (single) peut être acquis un nombre fini de fois (c’est la limite).
C’est un peu comme si vous étiez manager dans une agence immobilière, un propriétaire
vous donne une clé pour accéder à son appartement qu’il souhaite louer et vous en faites 5
copies pour permettre à vos 8 agents de pouvoir faire visiter l’appartement. Lorsque toutes les
clés ont été distribuées, il n’y en a plus de disponible pour certains agents.
Lorsque la limite du sémaphore est atteinte, on ne peut plus acquérir de sémaphore jusqu’à que
certaines taches en relâche (c’est-à-dire que les agents ramènent les clés permettront à
d’autres agents de faire visiter l’appartement).
Le noyau compte le nombre de fois que le sémaphore a été acquis (acquired) ou relaché
(released) en maintenant un compte (token count), qui est initialisé à une certaine valeur
lorsque le sémaphore est créé. Lorsque tache acquière un sémaphore, le compte (token count)
est décrémenté ; lorsqu’une tache libère un sémaphore, le compte est incrémenté.

1
Lorsque le compte arrive à 0, il n’y a plus de clés ou de jetons (token) disponibles. Une tâche
qui souhaite alors acquérir un sémaphore se trouve alors placée dans l’état bloqué si elle
choisit d’attendre pendant une certaine durée que le sémaphore devienne disponible.
La tasks waiting list enregistre toutes les taches bloquées qui sont en attente de la disponibilité
du sémaphore. Cette liste peut être ordonnées selon le type FIFO ou bien ordonnée selon la
priorité croissante des taches (la plus prioritaire sera la première à acquérir le sémaphore
disponible).
Lorsqu’un sémaphore indisponible devient disponible, le noyau autorise à la tâche en tête de la
tasks waiting list d’acquérir le sémaphore. Le noyau déplace alors cette tache vers l’état
running si elle a la plus grande priorité, ou vers l’état ready jusqu’à ce qu’elle devienne celle
de plus haute priorité tout en étant capable d’être exécutée. L’implémentation de la tasks
waiting list varie selon les noyaux.
Un noyau peut supporter différents types de sémaphores :

 Les sémaphores binaires (binary semaphore).


 Les sémaphores à compte (counting semaphore).
 Les sémaphores à exclusion mutuelle (mutex pour mutual exclusion semaphore).

1.1 Les sémaphores binaires.


Ils peuvent prendre la valeur 0 ou 1. Lorsque sa valeur est 0, il est considéré comme
indisponible ou vide (unavailable ou empty). Si sa valeur est 1, on dit qu’il est disponible
ou plein (available ou full). Lors de la création du sémaphore, il peut être initialisé à 0 ou à 1.
Son diagramme d’état est le suivant :

Les sémaphores binaires sont traités comme des ressources globales (global ressources), ce
qui signifie qu’ils sont partagés entre les taches qui en ont besoin. Le fait que ce soit une
ressource globale permet à n’importe quelle tache de le libérer, même si cette tâche ne
l’avait initialement acquis.

1.2 Les sémaphores à compte.


Il utilise un compteur count pour permettre son acquisition ou sa libération de plusieurs fois.
Lors de sa création, une valeur count lui est attribuée : elle indique le nombre de jetons
(token) disponibles. Si sa valeur initiale count est 0, le sémaphore créé est dans l’état
indisponible. Si, lors de sa création, sa valeur count est plus grande que 0, le sémaphore est
créé dans l’état disponible, le nombre de jetons disponible est donné par cette valeur.

2
Son diagramme d’état est le suivant :

Une ou plusieurs taches peuvent continuer à acquérir un jeton depuis le sémaphore à compte
jusqu’à ce que plus aucun jeton ne soit disponible. Lorsqu’il n’y a plus de jeton disponible,
count passe à 0, le sémaphore passe de l’état available vers l’état unavailable. Pour passer de
l’état unvailable vers l’état available, un jeton doit être libéré par n’importe quelle tâche. Le
sémaphore à compteur est également une ressource globale. Chaque libération d’un jeton
incrémente la valeur count.
Certaines implémentations peuvent permettre de limiter la valeur count à une valeur
maximale. On parle alors de compte borné (bounded count). Cette valeur est indiquée lors de
la création du sémaphore à compte. C’est le nombre de jetons maximal. Dans un sémaphore à
compte non borné (unbounded count), le nombre de jeton sera limité par les capacités de la
machine (représentation des types de données).

1.3 Les sémaphores Mutex.


Le mutex est un sémaphore binaire particulier qui supporte :

 La détention exclusive (ownership).


 L’accès récursif (recursive locking).
 La sécurité dans la suppression d’une tache (task deletion safety).
 Un ou plusieurs protocoles évitant les problèmes liés à l’exclusion mutuelle.

Son diagramme d’état est présenté page suivante.


Contrairement aux états available et unvailable des sémaphores binaires et à compte, pour
les mutex, les états sont appelés déverrouillé (0 ou unlocked) ou verrouillé (1 ou
locked). Un mutex est initialement créé dans un état unlocked, dans lequel il peut être
acquis par une tache. Après avoir été acquis, il passe dans l’état locked. Inversement,
lorsqu’ une tache relâche le mutex, il retourne dans l’état unlocked. Dans certains noyaux
les états locked et unlocked sont nommés acquired et released.

3
 La détention exclusive (ownership).
Elle est obtenue (ownership) lorsqu’une tache verrouille (lock) le mutex pour la
première fois en faisant son acquisition. Réciproquement, une tache perd l’ownership
du mutex lorsqu’elle le déverrouille en le relâchant.
Lorsqu’une tache détient le mutex, il n’est pas possible pour une autre tâche de le
détenir ou de le libérer. Ce n’est pas une ressource globale.

 L’accès récursif (recursive locking).


C’est la possibilité pour une tache détenant le mutex de l’acquérir plusieurs fois dans
l’état locked. En fonction des implémentions, l’accès récursif peut être automatique
lors de la création du mutex ou validé (ou pas) explicitement par le développeur.
Lorsque le recursive locking est disponible, on parle de recursive mutex. Ce type de
mutex est très utile lorsqu’une tache nécessitant un accès exclusif à une ressource
partagée appelle une ou plusieurs routines qui ont, elles aussi, besoin d’avoir accès à
cette même ressource partagée. Le recursive mutex permet des appels récursifs du
mutex et évitent le deadlock (c’est-à-dire deux ou plusieurs taches sont bloquées et en
attente de ressources bloquées).
Comme le montre la figure en haut de page, lorsqu’un mutex recursif est dans l’état
locked, le noyau enregistre la tâche qui l’a verrouillée (locked), et la considère comme
étant la propriétaire du mutex. Lors des prochaines tentatives, le noyau utilise un
compteur interne (count) associé au mutex pour relever le nombre de fois, que la
tache détenant le mutex, l’a acquis. Pour repasser dans l’état unlocked, le mutex doit
être relâché le même nombre de fois.
Dans cet exemple, le compteur (lock count) suit les deux états du mutex (0 pour
unlocked et 1 pour locked), ainsi que le nombre de fois qu’il a été verrouillée
récursivement (lock count >1). Dans d’autres implémentations, le mutex peut contenir
deux compteurs : un premier qui noté son état binaire (0 ou 1) et un second qui
maintient le nombre de fois qu’il a été acquis de manière récursive par la tâche.
Il est très important de noter que le compteur d’un mutex est non borné (unbounded).
Attention à ne pas confondre les compteurs associés aux mutex et ceux associés aux
sémaphores.

4
 La sécurité dans la suppression d’une tache (task deletion safety).
Cette caractéristique existe dans certaines implémentations. L’effacement prématuré
d’une tache est évité en utilisant des verrous, les task deletion locks, lorsqu’une tache
verrouille (lock) ou déverrouille (unlock) le mutex. Cela permet qu’une tache
détenant le mutex ne soit pas supprimée. Cette possibilité est validée ou pas lors de
la création du mutex, sous forme d’option.

 L’évitement de l’inversion de priorité (Priority Inversion Avoidance).


Ce phénomène apparait lorsque le système RTE est mal conçu. L’inversion de priorité
(priority inversion) apparait lorsque tache de plus haute priorité est bloquée, en attente
d’une ressource détenue par une tache de faible priorité qui est préemptée par une
tache de priorité moyenne.
La validation de certains protocoles appartenant aux mutex permet de résoudre cette
inversion de priorité. On trouve :
 Le protocole à héritage de priorité (priority inheritance protocol).
La tache 1 de niveau de priorité le plus faible qui a acquis le mutex voit sa
priorité s’élever au niveau de celle de plus haute priorité (tache 3) ayant
besoin du mutex (lors de l’inversion de priorité). La priorité de la tache 1
est ensuite ramenée à son niveau initial (le plus bas) après qu’elle ait
libéré le mutex qui est maintenant détenu par la tache 3.
 Le protocole à priorité plafond (ceiling priority protocol).
Il assure que la tâche qui vient de faire l’acquisition du mutex voit sa
priorité augmenter pour passer au-dessus des priorités de toutes les taches
en attente du mutex. Après que cette tache soit exécutée, elle retourne à sa
priorité initiale.

2. Opérations typiques sur les sémaphores.


Les opérations les plus courantes sont :

 La création et la suppression des sémaphores.


 L’acquisition et la libération du sémaphore.
 L’effacement d’une liste des taches en attente du sémaphore.
 L’obtention d’informations sur le sémaphore.

2.1. La création et la suppression des sémaphores.


Les opérations courantes sont :

Opération Description
Create Crée un sémaphore.
Delete Supprime un sémaphore.

5
Les appels au noyau pour la création des sémaphores sont fonction de leurs types :

 Binary : on doit spécifier dans son état initial et l’ordre de la liste d’attente.
 Counting : on doit spécifier sa valeur (count) initiale et l’ordre de la liste
d’attente.
 Mutex : on doit spécifier l’ordre de la liste d’attente et valider l’effacement
sécurisé des taches, la récursivité, et les protocoles anti inversion de sécurité (s’ils
sont disponibles).
Les sémaphores peuvent être supprimés à partir de n’importe quelle tache, en spécifiant
leur ID et en faisant un appel de suppression de sémaphore.
Supprimer un sémaphore n’est pas la même chose que le relâcher. Lors de l’effacement
d’un sémaphore, les taches bloquées dans la liste d’attente des taches sont débloquées et
placées soit dans l’état prêt, soit dans l’état d’exécution (si plus haute priorité). Toute
tâche qui tente d’acquérir un sémaphore supprimé retournera une erreur.
De plus, il ne faut pas effacer un sémaphore alors qu’il en train d’être utilisé (acquired).
Cette action peut provoquer la corruption de données ou d’autres problèmes sérieux si le
sémaphore protège l’accès à une ressource partagée ou une section de code critique.

2.2. L’acquisition et la libération du sémaphore.


Les opérations courantes sont :

Opération Description
Acquire Acquière un jeton sémaphore.
Release Libère un jeton sémaphore.

En fonction des noyaux et des types de sémaphores, on peut trouver d’autres noms que
acquire et release. Les taches qui demandent à acquérir un sémaphore peuvent le faire
de différentes façons :

 Wait forever : la tache teste bloquée jusqu’à ce qu’elle soit capable de faire
l’acquisition du sémaphore.
 Wait with a timeout : la tache teste bloquée jusqu’à ce qu’elle soit capable de
faire l’acquisition du sémaphore ou qu’un intervalle de temps (timeout
interval) soit épuisé. La tâche est alors retirée de la liste d’attente du
sémaphore, débloquée et placée soit dans l’état prêt, soit dans l’état
d’exécution (si plus haute priorité).
 Do not wait : la tache fait une requête pour acquérir un jeton sémaphore,
mais, si aucun jeton n’est disponible, la tâche ne se bloque pas.
Notez que les ISR (interruptions) peuvent aussi relâcher les sémaphores binaires et à
compte. La plupart des noyaux n’autorise pas les ISR à verrouiller ou déverrouiller
des mutex, ni à acquérir des sémaphores binaires ou à compte.
N’importe quelle tache peut relâcher un sémaphore binaire ou à compte. En revanche,
un mutex peut être relâché (unlocked) uniquement par la tâche qui en avait fait
l’acquisition (locked). Notez qu’un relâchement incorrect d’un sémaphore binaire ou à
compte peut générer une perte d’accès à des ressources partagées ou des E/S.

6
Par exemple, une tache obtient l’accès à une structure de données partagée en faisant
l’acquisition du sémaphore. Si une seconde tache relâche accidentellement ce
sémaphore, cela peut potentiellement une troisième tache de la liste d’attente du
sémaphore et permettre à cette troisième tache d’accéder à la même structure de
donnée. Avoir plusieurs taches essayant de modifier la même structure de données au
même moment peut corrompre les données.

2.3. L’effacement d’une liste des taches en attente du sémaphore.


Pour effacer toutes les taches en attente du sémaphore présentes dans la task waiting
list, on utilise l’opération suivante :

Opération Description
Flush Débloque toutes les taches en attente du sémaphore

Cette opération est utile pour diffuser un signal à un groupe de taches. Par exemple, le
développeur peut concevoir de multiples taches pour réaliser dans un premier temps
certaines activités, puis les bloquer en leur demandant d’acquérir un sémaphore qui
est rendu indisponible. Après que la dernière tache a effectué son travail, la tache peut
exécuter une opération flush sur le sémaphore commun, libérant toutes les taches de la
task waiting list. Il s’agit d’un scenario de synchronisation appelé thread rendez-vous.
.
Un thread rendez-vous est une situation dans laquelle de multiples taches en attente
d’exécution ont besoin d’attendre un instant pour synchroniser leur exécution.

2.4 L’obtention d’informations sur le sémaphore.


Cette opération est utile pour le débogage ou le monitoring. On trouve les opérations
suivantes :

Opération Description
Show info Montre l’opération générale sur le sémaphore
Show blocked tasks Donne la liste des ID des taches qui sont bloqués en
attente du sémaphore.

Ces opérations sont relativement rapides mais doivent être utilisées avec prudence car
les informations sur le sémaphore peuvent être dynamiques au moment de la requête.

3. Utilisations courantes des sémaphores.


Ils sont employés pour de synchroniser l’exécution des taches et coordonner l’accès
exclusif à des ressources partagées (shared ressources).
On présente à la suite des cas fréquents d’utilisation.

7
3.1. Attente de signal pour synchronisation (Wait-and-Signal Synchronization).
Deux taches peuvent communiquer pour se synchroniser sans échanger des données.
Par exemple, un sémaphore binaire peut être utilisé entre deux taches pour coordonner
le transfert du contrôle d’exécution, comme indiqué ci-dessous.

Dans cette situation, le sémaphore est initialement indisponible (valeur 0). La tâche
tWaitTask possède la plus haute priorité et s’exécute la première. Puis la tache fait
une requête pour acquérir le sémaphore mais se trouve bloquée car ce dernier est
indisponible. Cette situation offre la possibilité à la tache tSignalTask de priorité
inférieure de s’exécuter. A un certain moment, lors de son exécution, la tache
tSignalTask libère le sémaphore et débloque la tache tWaitTask. Comme la tâche
tWaitTask possède une plus grande priorité que la tache tSignalTask, dès que le
sémaphore est relâché, la tâche tWaitTask préempte la tache tSignalTask et
s’exécute immédiatement.
Un pseudo code est présenté à la suite.

3.2. Attente de signal pour synchronisation de plusieurs taches (Multiple Wait-and-


Signal Synchronization).
Pour coordonner la synchronisation de plus que deux taches, on utilise l’opération flush
sur une liste d’attente de sémaphore binaire, comme indiqué ci-dessous.

8
Dans cet exemple, le sémaphore est initialement indisponible (valeur 0). Les tâches
tWaitTask1, tWaitTask2, tWaitTask3 possèdent la plus haute priorité s’exécutent, elles
réalisent chacune un traitement. Puis, elles tentent d’acquérir le sémaphore qui est
indisponible et donc passent en mode bloqué. Cette situation offre la possibilité à la tache
tSignalTask de priorité inférieure de s’exécuter puis d’exécuter une opération flush sur le
sémaphore, débloquant les tâches tWaitTask1, tWaitTask2 et tWaitTask3. On suppose
dans le pseudo code qui suit les taches tWaitTask1, tWaitTask2 et tWaitTask3 ont le
même code.

Comme les tâches tWaitTask possèdent une plus grande priorité que la tache
tSignalTask, dès que le sémaphore est relâché, une des taches tWaitTask préempte
la tache tSignalTask et s’exécute immédiatement.

3.3 Poursuite de crédit pour synchronisation (Credit-Tracking Synchronization).


Parfois, le rythme d’exécution de la tâche tSignalTask qui émet un signal est plus
élevé que celui de la tâche tWaitTask qui reçoit ce signal. Dans ce cas, un mécanisme
est nécessaire pour compter chaque occurrence du signal. Avec un sémaphore à
compteur la tâche qui émet le signal continue de fonctionner à son propre rythme et
incrémente le compteur à chaque émission du signal, alors que la tâche en attente,
lorsqu’elle est débloquée, s’exécute à son propre rythme.

Le compte du sémaphore à compte est initialement à 0, rendant le sémaphore non


disponible. La tâche de plus faible priorité tWaitTask tente d’acquérir le sémaphore
mais bloque jusqu’à ce que la tâche tSignalTask rende le sémaphore disponible en le
relâchant. A ce moment, tWaitTask attend dans l’état ready jusqu’à ce que la tache

9
tSignalTask de plus haute priorité relâche la CPU en faisant un appel bloquant ou se
retardant. Un exemple de pseudo code est présenté.

Comme la tache tSignalTask a une plus haute priorité et s’exécute à son propre
rythme, elle peut incrémenter de multiple fois le sémaphore à compteur avant que la
tache tWaitTask commence le traitement de sa première demande. Le sémaphore à
compteur permet la création d’un crédit, c’est-à-dire du nombre de fois que la tache
tWaitTask peut s’exécuter avant que le sémaphore devienne indisponible.
Eventuellement, lorsque le rythme d’exécution de la tâche tSignalTask, donc le
rythme de relâchement du sémaphore diminue, la tache tWaitTask peut décrémenter
le compteur du sémaphore jusqu’à ce que ce nombre soit égal à 0 (empty). A ce
moment, la tache tWaitTask se bloque encore, attendant que la tâche tSignalTask
relâche le sémaphore.
Notez que ce mécanisme (Credit-Tracking Synchronization) est utile lorsque la tâche
tSignalTask les sémaphores en rafale, donnant une chance à la tache tWaitTask de
les récupérer un par un.
Une ISR peut aussi jouer le rôle d’une tache tSignalTask. Les interruptions possèdent
une priorité plus élevée que les taches.

3.4. Synchronisation pour un unique accès à ressource partagée (Single Shared-


Ressource-Access Synchronization).
Une des utilisations des sémaphores est de fournir un accès à une ressource partagée
(emplacement mémoire, structure de données, E/S). Le sémaphore permet de sérialiser
l’accès à cette ressource partagée, comme le montre la figure suivante.
Dans ce scénario, un sémaphore binaire est initialement créé dans l’état disponible
(available) donc sa valeur est à 1, il est utilisé pour protéger la ressource partagée.
Pour accéder à la ressource partagée, les taches 1 et 2 ont d’abord besoin d’acquérir le
sémaphore binaire avant de pouvoir lire ou écrire dans la ressource partagée.

10
Le pseudo code pour les taches tAccessTask1 et tAccessTask2 est identique, il est
présenté en dessous :

Ce code sérialise l’accès à la ressource partagée. Si la tache tAccessTask1 s’exécute


en premier, elle fait une requête pour acquérir le sémaphore et l’obtient car le
sémaphore est disponible. Une fois le sémaphore acquis, cette tache détient l’accès à la
ressource partagée et peut écrire ou lire cette ressource.
Entre temps, la tache tAccessTask2 de priorité supérieure se réveille et s’exécute suite
à un timeout ou un évènement externe. Elle tente d’acquérir le même sémaphore mais
est bloquée car ce dernier est détenu par la tache tAccessTask1. Après que la tache
tAccessTask1 relâche le sémaphore, la tache tAccessTask2 est débloquée et
commence son exécution.
Un des dangers de cette conception vient du fait que n’importe quelle tache peut
accidentellement relâcher le sémaphore binaire, même une tâche qui n’a jamais
acquis le sémaphore au départ. Si cela devait arriver, les taches t tAccessTask1 et
tAccessTask2 pourraient acquérir le sémaphore et donc lire ou écrire en même temps
dans la ressource partagée, ce qui provoquera un fonctionnement incorrect du
programme. Pour éviter ce problème, on utilisera le mutex : il comporte la notion de
propriété (ownership), ce qui assure qu’une une seule tache peut l’acquérir (locked) et
le relâcher (unlocked).

3.5. Synchronisation pour un accès recursif à ressource partagée (Recursive Shared-


Ressource-Access Synchronization).

Parfois le développeur souhaite qu’une tache puisse avoir un accès récursif à une
ressource partagée. Cette situation peut apparaitre si la tache tAccessTask appelle la
routine A qui appelle la routine B : La tâche et les deux routines ont alors besoin
d’accéder toute les trois à la ressource partagée. Cette situation est présentée à la suite.

11
Si on utilise dans ce scenario un sémaphore, la tache finira par se bloquer, provoquant
un deadlock. Lorsque la routine est appelée depuis la tâche, la routine devient une
partie de la tâche. Lorsque la routine A s’exécute, elle est donc une partie de la tache
tAccessTask. La routine A essayant d’acquérir le sémaphore et la même chose que la
tache tAccessTask essayant d’acquérir ce même sémaphore. Dans ce cas, la tache
tAccessTask finira par se bloquer, en attente d’un sémaphore indisponible qu’elle
possède déjà !
Une solution à ce problème est d’utiliser un mutex recursif (recursive mutex). Après
que la tache tAccessTask verrouille le mutex, elle le détient. Une tentative
supplémentaire, émanant de la tache ou de sa routine, d’acquérir le mutex provenant
se traduit par un succès. Lorsque les routines A et B tentent de verouiller le mutex,
l’opération se traduit par un succès sans aucun blocage.
Le pseudo code pour la tache tAccessTask, la routine A et la routine B est le
suivant :

12
3.6. Synchronisation pour des accès des ressources partagées (Multiple Shared-
Ressource-Access Synchronization).
Dans le cas où de multiples ressources de même nature sont partagées, un sémaphore à
compteur est utile, comme le montre la figure suivante.

Notez que ce scénario ne fonctionne pas si les ressources partagées ne sont pas de même
nature. Le compteur du sémaphore à compteur est initialement à égal au nombre de
ressources partagées de même nature, ici 2. Les deux premières taches souhaitant avoir un
jeton du sémaphore voient leurs demandes acceptées. La troisième tâche qui demande un
jeton se retrouve bloquée tant qu’une des deux premières tâches n’a pas libéré de jeton.
Le pseudo code des taches tAccessTask1 , tAccessTask2 , tAccessTask3 , demandant le
jeton est présenté à la suite ; on suppose que ces trois taches ont le même pseudo code.

Comme pour les sémaphores binaires, cette conception peut poser des problèmes si une tache
relâche un sémaphore qu’elle n’a pas initialement acquis. Dans le cas d’un code simple, ce
problème peut être réglé facilement. Si le code est plus élaboré, avec plusieurs taches
accédant à des ressources partagées avec de multiples sémaphores, les mutex peuvent fournir
une protection au niveau des accès aux ressources partagées.
Prenons le cas où un mutex est associé à chaque ressource partagée. Lorsqu’une tache tente de
verrouiller un mutex, elle essaye d’acquérir le premier mutex d’une manière non
bloquante. Si elle n’y arrive pas, elle essaye d’acquérir le second mutex d’une manière
bloquante. La figure suivante présente le pseudo code des taches tAccessTask1 ,
tAccessTask2 , tAccessTask3 dans ce cas ; on suppose que ces trois taches ont le même
pseudo code.

13
En utilisant ce scénario, les taches 1 et 2 arrivent à verrouiller un mutex et ont chacune
accès à une ressource partagée. Lorsque la tache 3 s’exécute, elle essaye d’acquérir le
premier mutex d’une manière non bloquante (le premier mutex est détenu par la tache 1).
Lorsque le premier mutex est débloqué, la tache 3 le bloque et a alors accès à la première
ressource partagée. Si le premier mutex est encore bloqué, la tache 3 tente d’acquérir le
second mutex, mais cette fois, de manière bloquante. Si le second mutex est aussi bloqué,
la tache 3 se bloque et attend que le second mutex soit débloqué.

14

Vous aimerez peut-être aussi