Académique Documents
Professionnel Documents
Culture Documents
1. Introduction.
La plupart des RTOS embarqués facilitent un environnement de développements multitâches ou
multithreads. De nombreux problèmes de conception apparaissent lors du développement
d’applications embarquées dans des systèmes multitâches.
La nature de ces environnements est que de multiples threads d’exécution partagent le même
ensemble de ressources. Le partage de ressources nécessite une coordination attentive pour
s’assurer que chaque tache puissent éventuellement acquérir la (ou les) ressource(s) dont elle a
besoin pour continuer son exécution.
Dans un environnement préemptif multitâches, le partage des ressources est fonction de la priorité
de la tâche. Plus la priorité de la tâche est importante, plus la tache le devient. Les taches de plus
hautes priorités ont une précédence sur les taches de plus basse priorité lors de l’accès aux
ressources partagées. Un partage de ressource ne peut pas violer cette règle. D’un autre côté, si les
taches de plus hautes priorités ont toujours le contrôle des ressources par rapport aux taches de
priorités inférieures, ce schéma de partage n’est pas équitable et peut même empêcher les taches
de plus faible priorité de s’exécuter. Cette situation est appelée famine ou privation (starvation).
La maximisation de l’utilisation des ressources est encore un autre problème.
Deux problèmes sont très fréquents lors du développement d’application. Il s’agit de l’inter
blocage (deadlock) et de l’inversion de priorité (priority inversion).
Ce chapitre présente :
1
Une ressource non-préemptive doit être volontairement relâchée par la tâche qui la détient, sinon
des résultats imprédictibles peuvent apparaitre. Une région mémoire partagée est un exemple de
ressource non préemptive. Par exemple, une tache une tache ne doit pas permettre une écriture
dans la mémoire partagée avant qu’une autre tâche ait terminé son opération de lecture ou écriture.
Le type de ressource détenu par une tache est important lorsque l’on choisit les solutions à adopter
dans le cas où une tache est impliquée dans des situations de deadlocks. La section suivante
présente les relations entre les types de ressources et les mécanismes de rétablissement (recovery)
dans le cas des deadlocks.
Une exclusion mutuelle : une ressource peut être accédée par une seule tache à un instant
donné, on parle de mode d’accès exclusif.
Aucune préemption : une ressource non préemptive ne peut pas être retirée de force de la
tache la détenant. Une ressource devient disponible uniquement si le détenteur libère
volontairement la ressource.
Maintient et attente : une tache maintient les ressources précédemment acquises, pendant
qu’elle attend que des ressources supplémentaires deviennent disponibles.
Attente circulaire : une chaine circulaire de deux ou plusieurs taches existe, dans laquelle
chaque tache maintient une ou plusieurs ressources qui sont demandées par la tache
suivante de la chaine.
Etant donné que chaque ressource est non préemptive et supporte uniquement le mode d’accès
exclusif, il est possible de représenter à la figure suivante une situation d’inter blocage entre deux
taches. Cette figure est appelée graphe de ressources.
Une flèche nommée holds dirigée depuis la ressource vers la tache indique que cette tache détient
la ressource. Une autre flèche, labélisée wants partant de la tache vers la ressource indique que la
tâche a besoin de cette ressource pour reprendre son exécution.
2
Dans cet exemple, la tache1 veut le scanner alors qu’elle détient l’imprimante. La tache1 ne peut
pas faire un traitement tant que l’imprimante et ne scanner ne sont pas en sa possession. La tache2
veut l’imprimante alors qu’elle détient le scanner. La tache2 ne peut pas faire un traitement tant
que l’imprimante et ne scanner ne sont pas en sa possession. Puisque ni la tache1, ni la tache2
souhaite céder la ressource qu’elle détient, les deux taches se retrouvent dans une situation d’inter
blocage car aucune des deux taches ne peut poursuivre son exécution.
Les dreadlocks peuvent impliquer plus que deux taches, comme indiqué à la figure suivante.
Dans le modèle de requête à ressource unique, comme présenté dans les deux figures
précédentes, une tache peut avoir en cours au plus une requête de ressource à un instant donné.
Dans ce modèle de requête, la tache demande la ressource sous la forme « souhaite une
imprimante ».
3
Dans le modèle de requête à ressource AND, une tache peut avoir en cours de multiples requêtes
simultanées à un instant donné. Par exemple, une tache faire une demande de ressource sous la
forme « R1 et R2 » ou « R1 et R2 et R3 ». Une tache est bloquée jusqu’à que toutes les ressources
lui sont attribuées. Dans ce modèle de requête, une tache demande les ressources sous la forme «
souhaite à la fois une imprimante et un scanner ».
Dans le modèle de requête à ressource OR, une tache peut demander un ensemble de ressources,
mais la tâche peut reprendre son exécution dès qu’une des ressources de l’ensemble demandé
devient disponible. Par exemple, une tache faire une demande de ressource sous la forme « R1 ou
R2 » ou « R1 ou R2 ou R3 ». Dans ce modèle de requête, une tache demande les ressources sous
la forme « souhaite soit une imprimante soit un scanner ». La tâche reprend son exécution
lorsqu’elle fait l’acquisition soit de l’imprimante, soit du scanner.
Dans le modèle de requête à ressource AND-OR, une tache peut demander des ressources sous
la forme de combinaisons de modèles AND et OR. Par exemple, une tache faire une demande de
ressource sous la forme « R1 ou R2 et (R3 ou R4) ». Dans ce modèle de requête, une tache
demande les ressources sous la forme « souhaite soit une imprimante soit un scanner, et souhaite
soit un buffer mémoire soit une FAM ». La tâche reprend son exécution lorsqu’elle fait
l’acquisition à la fois de l’imprimante et du buffer mémoire, lorsqu’elle fait l’acquisition à la fois
de l’imprimante et de la FAM, lorsqu’elle fait l’acquisition à la fois du scanner et de la FAM. Une
généralisation du modèle AND-OR est le modèle C(n,k). Dans ce modèle, une tache peut faire n
requêtes de ressource et reprendre son exécution dès que k ressources lui sont attribuées, avec k
inférieur ou égal à n.
4
Un système qui est capable de détecter un inter blocage est plus efficace en termes d’utilisation
des ressources en comparaison avec un système sans détection d’inter blocage. Un système
capable de détecter un inter blocage n’est pas conservatif lorsqu’il octroi des requêtes
d’allocation de ressource si un inter blocage est autorisé à apparaitre. Ces ressources sont
fortement utilisées. Un système sans détection d’inter blocage est conservatif lorsqu’il octroi des
requêtes d’allocation de ressource. Une requête de ressource est refusée si le système croit qu’il y
a un potentiel inter blocage, qui peut ne jamais arriver. Le conservatisme d’un système résulte en
des ressources au repos même si ces ressources pourraient être utilisées.
La détection d’inter blocage ne résout pas le problème. Au lieu de cela, l’algorithme de détection
informe l’algorithme de remise en état lorsque l’existence d’un inter blocage est découverte.
Pour l’inter blocage dans le modèle de requête à ressource unique, un cycle dans le graphe de
ressource est une condition nécessaire et suffisante.
Pour l’inter blocage dans le modèle de requête à ressource AND, un cycle dans le graphe de
ressource est une condition nécessaire et suffisante. Il est possible qu’une tache soit impliquée
dans plusieurs ensembles inter bloqués.
Pour l’inter blocage dans le modèle de requête à ressource OR, un nœud est une condition
nécessaire et suffisante.
Donc, la détection de l’inter blocage nécessite de trouver la présence d’un cycle dans le graphe de
ressource pour les modèles de requêtes de ressources unique et AND.
Pour l’inter blocage dans le modèle de requête à ressource AND-OR, aucune manière simple de
le décrire n’existe. Généralement, la présence d’un nœud après l’application de l’algorithme au
modèle OR puis au model AND et trouver un cycle est une indication de la présencence d’un inter
blocage.
Les sections suivantes présentent deux algorithmes de détection d’inter blocage. Un premier pour
le modèle de requête à ressource unique, et un second pour le modèle de requête à ressource
AND. Cela permet d’illustrer pratiquement ces algorithmes.
5
Dans la suite de la discussion, le terme nœud (node) fait référence soit au cercle (ressource)
soit au carré (task). Le terme arc (arc) fait référence à la flèche.
L’algorithme de détection d’inter blocage peut être décomposé en sept étapes :
1- Faire la liste de tous les nœuds, N, à partir du graphe.
2- Choisir un nœud parmi N. Créer une autre liste, L, initialement vide qui sera utilisée pour
le graphe transversal.
3- Insérer le nœud dans L et vérifier si ce nœud est déjà présent dans L. Si oui, un cycle
existe, donc un inter blocage est détecté et l’algorithme prend fin. Si non, retirer le nœud
de N.
4- Vérifier la présence d’arcs non traversent issu de ce nœud. Si tous les arcs sont traversés,
aller à l’étape 6.
5- Choisir un arc non traversent issu d’un nœud et le marqué comme étant traversé. Suivre
l’arc choisi vers le nouveau nœud et retourner à l’étape 3.
6- A ce niveau, un chemin prend fin dans le graphe, et aucun deadlock existe. Si il y a plus
d’une entrée dans L, retirer la dernière entrée de L. S’il reste plus d’une entrée dans L,
faire de la dernière entrée dans L le nœud courant et aller à l’étape 4.
7- Si la liste L n’est pas vide, aller à l’étape 2. Sinon, l’algorithme prend fin et il n’y a aucun
deadlock dans le système.
L’implémentation réelle de l’étape 3 à l’étape 6 se traduit d’abord par une recherche en
profondeur du graphe dirigé.
Appliquons cet algorithme au système précédemment présenté et recopié à la page suivante.
Etape 1 : N = {R1, T1, R2, T2, R3, T3, R4, T4, R5, T5, R6 }.
Etape 2 : L= {vide} ; choisir le nœud R1.
Etape 3 : L= {R1} ; aucun cycle est dans L ; N = {T1, R2, T2, R3, T3, R4, T4, R5, T5, T6}.
Etape 4 : R1 a un seul arc dirigé vers l’extérieur.
Etape 5 : Marquer l’arc ; il atteint le nœud T1 ; retourner à l’étape 3.
Etape 3 : L= {R1, T1} ; aucun cycle est dans L ; N = {R2, T2, R3, T3, R4, T4, R5, T5, T6}.
L’algorithme continue de l’étape 3 à l’étape 5, et se réitère jusqu’à atteindre le nœud T3, dans
lequel la liste L = {R1, T1, R2, T2, R4, T3} et la liste N = {R3, T4, T5, R5, T6}. Deux arcs
fuient le nœud T3. Lorsqu’on prend l’arc du bas, L = {R1, T1, R2, T2, R4, T3, R5}. Deux arcs
fuient le nœud T5. Lorsqu’on prend l’arc de droite, L = {R1, T1, R2, T2, R4, T3, R5, T6}.
6
Etape 4 : T6 ne possède pas d’arc fuyant. On continue à l’étape 6.
Etape 5 : Retirer T6 de la liste L qui devient L = {R1, T1, R2, T2, R4, T3, R5}. Retourner à
l’étape 4.
Etape 4 : prendre l’arc non marqué à gauche de R5.
Etape 5 : Marquer l’arc ; atteindre le nœud T5 ; retourner à l’étape 3.
Etape 3 : L = {R1, T1, R2, T2, R4, T3, R5, T5} et N= {R3, T4}. Aucun cycle n’est dans L.
Etape 4 : Prendre le seul arc fuyant à T5.
Etape 5 : Marquer l’arc ; atteindre le nœud R3 ; retourner à l’étape 3.
Etape 3 : L = {R1, T1, R2, T2, R4, T3, R5, T5, R3} et N= {T4}. Aucun cycle n’est dans L.
Etape 4 : Prendre le seul arc fuyant à R3.
Etape 5 : Marquer l’arc ; atteindre le nœud T1; retourner à l’étape 3.
Etape 3 : L = {R1, T1, R2, T2, R4, T3, R5, T5, R3, T1}. Le nœud T1 existe déjà dans L. Un
cycle est trouvé dans le graphe, un deadlock existe. L’algorithme se termine.
L’ensemble deadlock est constitué de tous les nœuds compris entre deux occurrences de T1,
T1 inclus. Ici, l’ensemble deadlock est donc {T1, R2, T2, R4, T3, R5, T5, R3}. L’algorithme
détecte les deadlocks s’ils existent. Le premier deadlock détecté dépend de la structure du
graphe. Un examen approfondi des ressources du graphe révèle l’existence d’un second
deadlock. Il s’agit de l’ensemble {R2, T2, R4, T3}. Au nœud T3, si l’arc vers le haut est
choisi plutôt que l’arc vers le bas, cette dernière occurrence de deadlock serait découverte, et
l’algorithme prendrait fin plus tôt.
7
Chaque ligne des tables C et D représente une tache T. Chaque colonne des tables C et D
représente un type de ressources. C est la table d’allocation des ressources, elle représente
les ressources déjà allouées. D est la table de demande des ressources, elle représente les
ressources additionnelles demandées par les taches.
Par exemple dans la table C, il y a C11 unités de ressource R1, C12 unité de ressources R2,
etc…, qui sont allouées à la tache T1. De manière identique, il y a C21 unités de ressource R1,
C22 unité de ressources R2, etc…, qui sont allouées à la tache T2. Par exemple, dans la table
D, la tache T1 demande D11 unités supplémentaires de la ressource R1, D12 unités
supplémentaires de la ressource R2, etc… dans le but de réaliser son exécution.
L’algorithme de détection d’inter blocages est le suivant :
1- Trouver une ligne i dans la table D, où D ij < A j pour 1≤ j< k . Si aucune ligne respectant
cette condition exister, le système est inter bloqué, et l’algorithme prend fin.
2- Marquer la ligne i comme étant complète et affecter A j= A j + D ij pour 1≤ j< k .
3- Si une ligne incomplète est présente, retourne à l’étape 1. Sinon, il n’existe pas d’inter
blocage dans le système et l’algorithme prend fin.
L’étape 1 de l’algorithme recherche une tache dont les besoins en ressources peuvent être
satisfaits. Si une telle tâche existe, la tache peut s’exécuter jusqu’à sa fin. Les ressources des
taches qui ont pris fin sont libérées et réintègrent l’espace des ressources disponibles, ce qui
correspond à l’étape 2. Les ressources nouvellement disponibles peuvent être utilisées pour
satisfaire les besoins des autres taches, ce qui leur permettra de reprendre leur exécution et de
prendre fin.
Lorsque l’algorithme prend fin, le système possède un inter blocage si la table T possède des
lignes incomplètes. Les lignes incomplètes représentent les taches appartenant à l’ensemble
inter bloqué (deadlocked set).
L’algorithme est illustré à la page suivante.
8
Etape 1 : la tache 1 ne peut pas continuer car les ressources disponibles ne satisfont pas ses
besoins. La tache 2 peut continuer car elle dispose de ce dont elle a besoin.
Etape 2 :
Etape 3 : Il reste la tache 1, la tache 3 et la tache 4. On retourne à l’étape 1.
Etape 1 : la tache 1 ne peut pas continuer. La tache 3 peut continuer car elle dispose de ce
dont elle a besoin.
Etape 2 :
Etape 3 : Il reste la tache 1 et la tache 4. On retourne à l’étape 1.
Etape 1 : la tache 1 ne peut pas continuer. La tache 4 peut continuer car elle dispose de ce
dont elle a besoin.
Etape 2 :
Etape 3 : Il reste la tache 1. On retourne à l’étape 1.
Etape 1 : la tache 1 peut continuer
Etape 2 :
Etape 3 : Il ne reste plus aucune étape, l’algorithme prend fin. Il n’y a pas d’inter blocage dans
le système.
Maintenant, si la demande de ressources pour la tache 3 était [0 1 1] au lieu de [0 1 0], les
taches 1, 3 et 4 ne pourraient reprendre leur exécution en raison d’un manque de ressources.
Dans ce cas, ces trois taches seraient en inter blocage.
Il est important de noter que l’exécution de l’algorithme de détection d’inter blocage prend du
temps et peut être non déterministe.
9
cette remise en état. Parfois, il est nécessaire d’exécuter de multiples méthodes de remise en
état avant de résoudre le deadlock, comme illustré plus loin.
Pour des ressources préemptives, la préemption des ressources est une façon de recouvrer la
situation normale à partir d’un deadlock. L’ensemble inter bloqué est transféré à l’algorithme
de remise en état après que l’algorithme de détection ait construit cet ensemble. L’algorithme
de remise en état peut alors exercer la préemption en prenant des ressources à des taches et en
les donnant à d’autres taches. Cette technique brise temporairement le deadlock. Cette
dernière peut compléter son exécution et libérer ses ressources. Ces ressources sont alors
utilisées pour satisfaire les besoin de la première tâche et l’amener à la fin de son exécution.
La préemption des ressources sur des ressources préemptives n’affecte pas directement l’état
d’exécution de la tache ou son résultat, mais cette préemption influe sur les contraintes
temporelles de la tâche. La durée de la préemption des ressources peut introduire chez la tache
préemptée un abandon, qui se traduit par une exécution incomplète et indirectement affecte le
résultat de la tâche.
Pour des ressources non préemptives, la préemption des ressources peut se faire au détriment
de la tache préemptée et peut possiblement aussi affecter les résultats des autres taches. Par
exemple, considérons la situation dans laquelle une tache est au milieu de l’écriture de
données dans une zone mémoire partagée, alors que pendant le même temps une seconde
tache nécessite un accès en lecture à partir dans la même zone mémoire. L’opération d’écriture
n’est pas valable, lorsqu’une autre tâche crée un inter blocage, et le système revient en état
depuis l’inter blocage en préemptant la ressource de la tache d’écriture. Lorsqu’la seconde
tache possède la ressource et commence à accéder à la mémoire partagée, la donnée est
incohérente et inconsistante. Pour cette raison, la zone mémoire partagée est classée comme
ressource non préemptive. La tache préemptée écrit le reste de la donnée, lorsque l’accès à la
mémoire partagée est retourné. La donnée n’est plus utile, et l’opération d’écriture est un
effort inutile. Parfois, ce type de préemption de ressource est aussi valable que de supprimer la
tache préemptée du système.
D’un autre côté, les effets de la préemption de ressources non préemptives peuvent être
éliminés si la tache possède en interne un mécanisme d’auto remise en état. Une tache peut
réaliser une auto remise en état en définissant des points de contrôle lors de son chemin
d’exécution. Dès que la tache atteint un point de contrôle, la tache change un état global pour
refléter cette transition. De plus, la tache doit définir un point d’entrée à invoquer par
l’algorithme de remise en état après que la tache ait le droit de reprendre son exécution. Le
point d’entrée n’est rien d’autre que le début de la routine interne d’auto remise en état. En
général, la remise en état implique de revenir au point de contrôle précédent et de ré exécuter
le code à partir de ce point. Ce concept est illustré au listing suivant :
10
Dans le précédent listing, une préemption de ressource est réalisée sur la tâche qui écrit et la
ressource préemptée est donnée à la tâche qui lit. Le mécaniste d’auto remise en état de la
tâche qui écrit implique de retourner au point de contrôle précédent et peut être de répéter
l’écriture d’opération, suivie par une notification diffusée (à toutes les autres tâches qui
utilisent la zone mémoire partagée) indiquant une mise à jour de la zone mémoire partagée.
Cette méthode réduit l’impact sur les autres taches.
La réaffectation des ressources préemptées joue un rôle important dans la destruction de l’inter
blocage. Par exemple, supposons la découverte d’un ensemble inter bloqué {T1, R2, T2, R4,
T3, R5, T5, R3} comme indiqué figure suivante.
Le problème n’est pas résolu car un nouvel inter blocage est généré par cette affectation de
ressource. Au lieu de cela, si R2 est d’abord attribué à T1, le deadlock est détruit comme
indiqué figure suivante :
11
En conséquence, T1 peut s’exécuter et prendre fin, puis libérer R1, R2 et R3. Cette
situation permet à T5 de s’exécuter et de relâcher R5. Alors, R2 et R5 deviennent
disponibles pour T2, qui a la permission de s’exécuter jusqu’à son terme. Finalement,
T2 se voit offrir une seconde chance d’exécution, et le deadlock est éliminé par une
réaffectation propre des ressources.
4. Inversion de priorité.
12