Les solutions matérielles
Nous pouvons distinguer dans cette catégorie plusieurs solutions. Elles s’appuient toutes sur un dispositif matériel.
Désarmement des interruptions
L'idée consiste tout simplement à ne pas allouer l’unité centrale à un autre processus tant que le processus courant dans une section critique. De cette façon, nous avons la certitude que la section critique va s’exécuter de manière indivisible.
Comme l’allocation de l’unité centrale est la conséquence à la prise en compte d’une interruption non masquée, alors il suffit pou résoudre le problème de masquer les interruptions pendant la durée de la section critique.
Les deux sections de ce protocole se définissent de la manière suivante :
Prologue (section d’entrée) : Masquer les interruptions.
Epilogue (section d’entrée) : Démasquer les interruptions.
Le comportement des processus est décrit par le schéma suivant :
while(1)
{
< Section Restante >
Masquer les interruptions ;
< Section Critique >
Démasquer les interruptions ;
}
Malgré sa simplicité, la solution précédente souffre d’un certain nombre d’inconvénients :
- Cette solution est inadaptée à certains systèmes multiprocesseurs (quand les interruptions ne sont pas gérées par le même processeur) : Si les interruptions sont masquées (désarmées) sur un processeur particulier, un processus s’exécutant sur un autre processeur peut parfaitement accéder à une de ses sections critiques et manipuler ainsi les variables communes.
- On peut retarder des processus prioritaires par l’exécution d’une longue section critique malgré que ces processus prioritaires puissent ne pas vouloir exécuter de sections critiques.
Test-And-Set
Cette solution est basée sur l’utilisation d’une instruction permettant de lire et d’écrire le contenu d’un mot mémoire de manière indivisible. Cette instruction s’appelle Test-And-Set (TAS).
L’action réalisée par cette instruction est donnée par le pseudo-code suivant :
Void TAS(int *a, int *b)
{
*a = *b;
*b = 1;
}
Un protocole, utilisant cette instruction, a été mis au point pour résoudre le problème de la section critique. La mise en œuvre de ce protocole nécessite l’utilisation de :
- Une variable commune, partagée entre tous les processus, appelée verrou. Elle est initialisée à 0.
- Une variable testi, locale à chaque processus Pi.
Verrou == 1 si la section critique est verrouillée, 0 sinon.
Le processus entrant dans la section critique verrouille son accès (positionner verrou à 1) tant qu’il est à l’intérieure interdisant ainsi les autres d’y entrer. A sa sortie, il déverrouille l’accès en remettant verrou à 0.
Les deux sections de ce protocole se définissent de la manière suivante :
Prologue (section d’entrée) : TAS(&testi , &Verrou)
While(testi ==1) TAS(&testi , &Verrou);
Epilogue (section d’entrée) : verrou = 0 ;
Le comportement des processus est décrit par le schéma suivant :
while(1)
{
< Section Restante >
TAS(&testi , &Verrou)
While(testi ==1) TAS(&testi , &Verrou);
< Section Critique >
verrou = 0 ;
}
La validité de cette solution est fondée sur le caractère indivisible de l’instruction TAS.
Le premier processus réussissant à exécuter (de manière indivisible) cette instruction, trouve la variable verrou à 0 et entre, par conséquent, dans sa section critique. Pendant sa présence à l’intérieur de la section critique, les processus désirant accéder à la section critique trouvent verrou == 1 et doivent ainsi attendre. En effet, cette variable n’est pas remise à 0 qu’à la sortie de la section critique. A partir de cet instant, le premier qui exécute l’instruction TAS entre à son tour dans la section critique.
L’exclusion mutuelle est assurée mais les autres conditions ne sont pas remplies :
- L’attente bornée n’est pas garantie.
- L’implantation de cette instruction dans un système à plusieurs processeurs nécessite le verrouillage du bus mémoire à chaque exécution de l’instruction.