Définition de la synchronisation :
La synchronisation (sun, « ensemble » et khrónos, « temps ») est l'action de
coordonner plusieurs opérations entre elles en fonction du temps. On appelle
synchronisation la mise en œuvre des relations entre processus en vue de Respecter
les contraintes de coopération.
2. Exclusion mutuelle
Les processus disposent chacun d'un espace d'adressage protégé inaccessible aux autres
processus. Pour communiquer et s'échanger des données, les processus peuvent utiliser des
outils de communications offerts par le système. La manipulation de ces outils de
communication doit se faire dans le respect de règles de synchronisation qui vont assurer que
les données échangées entre les processus restent cohérentes et ne sont pas perdues. Un
premier problème de synchronisation est celui de l'accès par un ensemble de processus à une
ressource critique , c'est-à-dire une ressource à un seul point d'accès donc utilisable par un
seul processus à la fois.
Définition de la ressource critique : Les ressources partagée, comme des zones
mémoire, cartes d'entrées/sorties, etc., sont dites critiques si elles ne peuvent être
utilisées simultanément que par un seul processus. On appelle ressource critique tout
objet : variable, table, chier, périphérique, ... qui peut faire l'objet d'un accès
concurrent (ou simultané) par plusieurs processus.
Définition de la section critique : Une SC est une suite d'instructions qui opèrent sur
un ou plusieurs ressources partagées (critiques) et qui nécessitent une utilisation
exclusive de ces ressources. Les portions de code qui manipulent des ressources
critiques sont appelées des sections critiques. Ces sections doivent être exécutées en
exclusion mutuelle.
1
Les problèmes d'incohérences des résultats, posé par les accès concurrents, montrent que la
solution consiste à exécuter les sections critiques en exclusion mutuelle. C'est à dire qu'une
section critique (SC) ne peut être entamée (ou exécutée) que si aucune autre SC du même
ensemble n'est en exécution.
On ajoute un protocole avant et après chaque section critique .Ces protocoles assureront
l’exclusion mutuelle.
Hypothèse 1: tout processus sort de section critique au bout d'un temps fini .
2
3. Conditions nécessaires pour réaliser une exclusion mutuelle :
Une solution au problème de la section critique doit satisfaire aux 4 besoins suivants :
Les différentes solutions apportées au problème de l’exclusion mutuelle que nous verrons
peuvent être classées selon l’attente des tâches pour la ressource et selon l’environnement sur
lequel ces tâches s’exécutent. 2 grandes classes de solutions envisageables :
Demande de la ressource
Libération de la ressource
3
5. Solutions d’attente active :
a) Solutions matérielles avec attente active : Il existe des solutions qui utilisent des
dispositifs matériels particuliers pour résoudre le problème de la section critique.
Ces solutions sont utilisées dans la pratique sur systèmes monoprocesseurs
centralisés.
Exclusions mutuelle par masquage des interruptions :
Protocole d’entrée en SC : masquer les interruptions, La SC ne peut être
interrompue car les changements de contexte sont impossibles
Protocole de sortie de SC : démasquer les interruptions
Processus P :
section_critique;
hors_section_critique;
fait;
Critiques de la solution :
4
Réalisation d’un verrou : Un verrou est une variable booléenne partagée
-verrou=vrai la ressource critique n’est pas libre
-verrou=faux la ressource critique est libre
Le problème : acquisition atomique du verrou!
En général, il existe des instructions câblées telles que Swap qui échange de manière
atomique deux variables, Test and Set qui teste et modifie de manière atomique une variable.
Réalisation d’un verrou avec swap atomique :
procédure swap (booléen x,y)
Booléen copie=x;x=y;y=copie;
Contexte commun :
Booléen verrou=faux; //variable partagée initialisée
Processus P
//entrée en section critique
Booléen macopie;
tant que (vrai) faire
macopie=vrai;
tant que (macopie) faire
swap(macopie,verrou);
fait;//attente active
section critique;
// sortie de section critique
verrou=faux;
5
Cette solution est valable et efficace, même dans le cas d’un système multiprocesseur, mais le
problème c’est l’attente active.
Algorithme :
Processus P0
tant que vrai faire
//entrée en SC
C[0]=vrai;
tant que (C[1])faire //P1 est en SC ou demande à
y entrer
tant que(T==1)faire C[0]=faux; //P1 est prioritaire
C[0]=vrai; //T=0
Section_critique;
//sortie de SC
C[0]=faux;
T=1;
Hors SC;
Fin
Processus P1
tant que vrai faire
//entrée en SC
C[1]=vrai;
tant que (C[0])faire //P1 est en SC ou demande à y
entrer
tant que(T==0) faire C[1]=faux; //P1 est prioritaire
C[1]=vrai; //T=1
Section_critique;
//sortie de SC
C[1]=faux;
T=0;
Hors SC;
Fin
6
Algorithme de Peterson : Peterson propose longtemps après Dekker une solution
plus simple d'exclusion mutuelle par attente active.
Algorithme
Processus P0
tant que (vrai)faire
//entrée en SC
C[0]=vrai;T=1;
tant que (C[1] and T==1); //attente en cas de conflit
Section_critique;
//sortie de SC
C[0]=faux;
Hors SC;
Fin
Processus P1
tant que (vrai)faire
//entrée en SC
C[1]=vrai;T=0;
tant que (C[0] and T==0); //attente en cas de conflit
Section_critique;
//sortie de SC
C[1]=faux;
Hors SC;
Fin
7
begin
choosing[0..n-1]:=faux;
number[0..n-1] := 0;
process Pi(1..n)
while (true)do{
{ choosing[i] := vrai;
number[i] := max(number[0], ..., number[n-1])+1;
choosing[i] := faux;
for (j:=0 to n-1)
{ while choosing[j] do /*rien*/;
while (number[j]!=0) and
((number[j],j)<(number[i],i)) do /*rien*/;
}
.... section critique
number[i] := 0;
.... section non-critique
};
}
end
Remarque: Le problème de toutes les approches décrites ci-dessus est : qu’elles mettent en
œuvre des mécanismes d’attente active : les processus désirant entrer en section critique
consomment énormément de ressource processeur, et font un usage intensif du bus mémoire.
Il faut donc mettre en œuvre des mécanismes d’exclusion mutuelle qui bloquent les processus
en attente.
8
– Un consommateur doit arrêter de consommer des choses quand le tampon est
vide.
La solution avec les primitives SLEEP&WAKEUP
Initialisation
N : taille de tampon
Compteur : indice de nombre d’objets dans le tampon =0 ;
Producteur Consommateur
Int objetP
Int objetM ;
While (true) { While(true) {
9
Les sémaphores
(Solution d’attente passive)
I. Introduction :
Un sémaphore sem est une structure système composée d’une file d’attente f(s)
de processus et d’un compteur C(s), appelé niveau du sémaphore et contenant
initialement une valeur C0(s). Cette structure ne peut être manipulée que par trois
opérations P(sem), V(sem) et init (sem, C0(s)). Ces trois opérations sont des
opérations indivisibles c'est-à-dire que l’exécution de ces opérations s’effectue avec
interruptions masquées et ne peut être interrompue.
II. Les opérations sur les sémaphores :
a) L’opération init :
Elle a pour but d’initialiser le sémaphore, c'est-à-dire qu’elle met à vide la file d’attente f(s) et
initialise avec la valeur C0(s) le compteur C(s). on définit ainsi le nombre de jetons initiaux
dans le sémaphore.
b) l’opération P(s) :L’opération P(s) attribue un jeton au processus appelant s’il en reste
au moins un et sinon bloque le processus dans la file f(s). L’opération P est donc une
opération éventuellement bloquante pour le processus élu qui l’effectue. Dans le cas
du blocage, il y a ré ordonnancement et un nouveau processus prêt est élu.
Concrètement, le compteur C(s) du sémaphore est décrémenté d’une unité. Si la
valeur du compteur devient négative, le processus est bloqué.
S : sémaphore
P(S) : Début
Masquer-it
S.C=S.C-1;
If( S.C<0) alors
début
État(Pi):= bloqué;
Mettre Pi dans S.F //file des
processus bloqué sur S
démasquer-it ;Fin
10
c) L’opération V(s) :
V(s) :
Début
Masquer-it
S.C :=S.C+1
Si S.C<=0 alors
Début
Sortir P(en-tête) de S.F
Etat (P) :=actif
fin
démasquer-it
fin
Remarque : On dit qu'un sémaphore est binaire si son compteur ne peut prendre que les
valeurs 0 ou 1,Si Val(S) est initialise a 1 alors la section critique est exécutée en
Exclusion mutuelle.
III. L’exclusion mutuelle à l’aide des sémaphores : Sémaphores d'Exclusion Mutuelle : But
: protéger l'accès à une ressource unique.
Exemple :
Soient N processus se partageant une ressource critique. Un processus Pi s'écrira :
Contexte commun :
IV. Désavantage:
Motivation = les sémaphores peuvent être utilisés pour résoudre à peu près n’importe quel
problème d’exclusion mutuelle ou synchronisation . . . mais, les sémaphores possèdent
certains désavantages:
mécanisme qui demande une discipline sévère dans la façon dont ils sont
Utilisés, sous peine d’erreurs: que se passe-t-il si on oublie d’indiquer un appel `a V? Ou si
11
On effectue une action P en trop?
Mécanisme sans localisé: un sémaphore doit être connu et accessible par tous les
processus qui pourraient devoir l’utiliser doit être une variable globale tout le programme
Doit être examiné pour voir ou et comment le sémaphore est utilisé
(libérer le sémaphore) V (s) réveille un processus en attente sur s s’il existe un tel
processus, sinon on incrémente s
2) Le sémaphore binaire :
Un sémaphore binaire revient donc à considérer un sémaphore initialisé à 1.
Un sémaphore binaire est un sémaphore ayant une valeur entière entre 0 et 1.
un sémaphore binaire peut étre plus simple à implémenter qu’un sémaphore de
comptage(généralisé) Pour implémenter un sémaphore généralisé (comptage) avec des
sémaphores binaires nous avons besoin des structures de données suivantes :
C : integer ;
P(S): V(S):
Debut Debut
P(S3) P (S1)
C:=C+1;
P(S1) If C<=0 then V(S2);
C:=c-1; V S1 ;
If c<= 0 then
Begin End
V(S1)
P( S2)
End
Else
V(S1)
V(S3)
End
12
3) Sémaphores privés:
Un sémaphore ne contient qu’un compteur, c’est parfois insuffisant pour exprimer le
contrôle d’un problème concurrent.
Dans le schéma des sémaphores privés, l’action de synchronisation est associée non plus à
une ressource mais à un processus. La condition de blocage peut alors être propre au
processus
Définition : un sémaphore privé spriv d’un processus Pi est tel que seul Pi peut exécuter
P(spriv) et V(spriv) ,les autres processus ne peuvent exécuter que V(spriv)
Un processus serveur dont le rôle est d’offrir un service à des processus clients ne peut
s’exécuter que sur demande des clients, il traite une seule demande à la fois.
P(spriv);
// service
…fin;}
Processus client1
...
V(spriv);
...
Processus client2
...
V(spriv);
...
Conclusion :
Le sémaphore est un mécanisme qui permet le blocage et le réveil explicites de processus
Le sémaphore permet d’associer un nombre d’autorisations d’accès disponibles à un objet
partagé
Le schéma des sémaphores privés permet d’exprimer une condition d’accès propre à un
processus
Tout paradigme de la concurrence peut être traité par l’emploi de sémaphores
Le sémaphore est un mécanisme simple et puissant, mais “ dangereux” dans un emploi
non contrôlé
13
Les Moniteurs de Hoare
(Solution d’attente passive)
I. Introduction :
Les sémaphores peuvent être utilises pour resoudre à peu près n’importe quel problème
d’exclusion mutuelle ou synchronisation . . . mais, les sémaphores possèdent certains
désavantages:
Le sémaphore est un mécanisme simple et puissant, mais “ dangereux” dans un emploi non
contrôlé avec le respect des spécifications de comportement : pas de boucle infinie, ni de
blocage ou panne pendant l’utilisation des objets partagés
Problème : si plusieurs processus peuvent exécuter des méthodes sur un même objet, le
problème de gérer l’interaction entre ces processus se pose .
Solution : il faut assurer une certaine atomicité garantissant l’exécution correcte des
opérations (méthodes)
Un mécanisme permettant la mise en attente de processus par rapport à une condition sur
l’état de l’objet partagé est nécessaire.
L’idée des moniteurs est de regrouper dans un module spécial, appelé moniteur, toutes
les sections critiques d’un même problème. Un moniteur est un ensemble de procédures, de
variables et de structures de données. Les processus peuvent appeler les procédures du
moniteur mais ils ne peuvent pas accéder à la structure interne du moniteur à partir des
procédures externes.
Définition : un moniteur est un outil évolué de synchronisation .un moniteur est une
classe utilisée dans un contexte de parallélisme. Les instances de la classe seront des
objets utilisés simultanément par plusieurs processus.
III. Les éléments de base d’un moniteur :
Un moniteur =
14
IV. La structure d’un moniteur :
L’exclusion mutuelle est supportée de façon implicite: un appel, par un processus, d’une
procédure exportée par le moniteur assure que la procédure sera exécutée de façon exclusive,
C’est- a-dire, au plus un appel d’une procédure du moniteur sera actif a un instant donne.
Pour assurer l’exclusion mutuelle, il suffit qu’il y ait à tout moment au plus un processus
actif dans le moniteur. C’est le compilateur qui se charge de cette tâche. Pour ce faire, il
rajoute, au début de chaque procédure du moniteur un code qui réalise ce qui suit : s’il y a
processus actif dans le moniteur, alors le processus appelant est suspendu jusqu’à ce que le
processus actif dans le moniteur se bloque en exécutant un wait sur une variable
conditionnelle (wait(c)) ou quitte le moniteur. Le processus bloqué est réveillé par un autre
processus en lui envoyant un signal sur la variable conditionnelle ( signal(c) ).
Cette solution est plus simple que les sémaphores et les compteurs d’événements, puisque le
programmeur n’a pas à se préoccuper de contrôler les accès aux sections critiques.
15
VII. Le moniteur et la synchronisation (coopération) :
Les conditions :
Les moniteurs possèdent un type condition. Les variables de ce type ne peuvent être déclarées que
dans un moniteur. Une condition est similaire à un sémaphore. Il y a deux opérations permises avec les
conditions: l'opération wait qui suspend le processus et l'opération signal qui réveille un processus.
Une variable condition est une variable :
qui est définie à l’aide du type condition;
qui a un identificateur mais, qui n'a pas de valeur (contrairement à un sémaphore).
Une condition : ne doit pas être initialisée
ne peut être manipulée que par les primitives Wait et Signal.
est représentée par une file d'attente de processus bloqués sur la même cause; est
16
alors deux processus qui pourraient s'exécuter dans le moniteur. Il est cependant important
qu'un seul des processus puisse pour suivre son exécution pour assurer l'exclusion mutuelle. Il
y a alors deux choix possible conduisant à deux implantations possibles des moniteurs :
1) le choix N° 1 : le processus qui a effectué l'opération signal, P1, continue son exécution.
2) le choix N°2 : le processus réveillé, P2, a accès au moniteur et continue son exécution. P1
pourra continuer son exécution quand P2 aura quitté le moniteur. ( P2 a été réveillé par P1
quand certaines conditions étaient satisfaites, si on attend trop longtemps pour réveiller P2, il
se peut que ces conditions ne soient plus satisfaites. Du point de vue de la vérification de
l'exactitude d'un programme, il est préférable de réveiller le processus immédiatement après
l'instruction signal.)
Exemple 1 :
Un RDV entre N processus à l’aide des moniteurs
………..
}
Exemple 2 : Le repas des philosophes à l’aide des moniteurs
17
Début
état[i]=demande;
si (état[(i+1)%5]≠mange et état[(i-1)%5]≠mange)
alors état[i]=mange;
sinon AMoi[i].Wait; finsi;
Fin
Procedure Entry Rendre(entier i);
Début
état [i]=pense;
{réveil éventuel d’un voisin}
si (état[(i+1)%5]=demande et état[(i+2)%5]≠mange)
alors état[(i +1)%5]=mange; AMoi[(i+1)%5].Signal;
sinon
si (état[(i-1)%5]=demande et état[(i-2)%5]≠mange)
alors état[(i-1)%5]=mange; AMoi[(i-1)%5].Signal ;finsi;
finsi;
Fin
Début {Initialisations }
Pour i de 0 à 4 faire état[i]=pense; fait;
Fin
Processus Philosophe i
entier i= numero du processus ;
Début
tant que vrai faire
penser;
RepasPhilosophes.prendre(i);
manger;
RepasPhilosophes.rendre(i);
fait;
Fin
18
La région critique
(Solution d’attente passive)
Introduction :
Les régions critiques ont été définies par Hoare et Brinch-Hansen. L'idée de base est de
regrouper les données à protéger en exclusion mutuelle, de caractériser syntaxiquement ce
groupe, et d'en contrôler l'accès
Les régions critiques constituent des outils de haut niveau permettant de résoudre des
problèmes d'exclusion mutuelle et de synchronisation. Faciles d’utilisation, les régions
critiques sont coûteuses en temps d'exécution.
La distinction entre ces deux sortes de variables se fait par le mot clés partagée
(SHARED). On peut indiquer lors d'une déclaration de type que les variables de ce type
sont partagées, et donc, nous le verrons, ne peuvent donc être accédées par les processus
parallèles que de l'intérieur de régions mentionnant ces variables (Une région est une
instruction structurée particulière du langage)
L'idée est de regrouper toutes les instructions manipulant les variables partagées, et
formant donc une section critique, dans des régions mentionnant ces variables.
2. La syntaxe de la région critique : Nous donnerons pour une région critique la syntaxe
suivante:
VAR v : partagée T ;
REGION v DO S /*suite-d'instructions */
END REGION;
La variable est déclarée avec l'attribut partagée (shared) , qui signifie partagée. Cet
attribut limite l'accès à la variable. Le type, noté t, est un type quelconque, et peut être en
particulier un tableau ou un enregistrement (record).
19
de l'exclusion mutuelle, si des processus sont en attente sur cette variable, ils seront relancés
automatiquement.
de l'exclusion mutuelle, si des processus sont en attente sur cette variable, ils seront relancés
automatiquement
Exemple1 :
REGION compteur DO
compteur ++;
END REGION;
Exemple 2:
REGION compteur DO
compteur --;
END REGION;
Syntaxes 2:
region R do S ;
Pour synchroniser des processus, il faut utiliser une variable partagée (relance
automatique). Donc, tout accès à cette variable est protégé par une exclusion
mutuelle ;
La lisibilité est bien meilleure qu'avec les méthodes précédentes ;
Le compilateur évite de nombreuses erreurs (mauvais appariement de P et V,
blocage en section critique...)
3. La région critique et l’exclusion mutuelle : Il y automatiquement en entrée d'une région
demande d'exclusion mutuelle, et en sortie libér .Pendant l’exécution de ≪S≫ aucun autre
processus ne peut accéder la ressource Ration de l'exclusion. équivalente à :
P(mutex) ;
S;
V(mutex) ;
20
4. La région critique et la synchronisation :
Les régions critiques ne permettent pas de synchronisation conditionnelle , Pour y
arriver on ajoute un énoncé « when »
La région critique conditionnelle :
Syntaxe1 : region v when b do s ;
Syntaxe 2 : var v1, v2, ..., vn : T;
ressource R : v1, v2, ..., vn
region R when B do S
Le processus qui l'exécute entre en section critique ; il calcule la valeur de
l'expression booléenne b :
L’´évaluation de B et l’exécution de S sont ininterruptibles.
Si B est faux, on bloque le pcs et on relâche l’exclusion
Mutuelle
il faut noter que :
les régions critiques, tout comme les sémaphores, font partie du texte des processus ;
lorsqu'un processus a été bloqué sur condition fausse, sa réactivation répand de
l'évolution des variables dans les autres régions. Sans une analyse fine du contenu des
régions, il faut le réactiver à chaque fois qu'un processus sort d'une région critique sur
la même variable, refaire le test et, si la condition est toujours fausse, le ré bloquer
Exemple : philosophe
processus Philosophe
while(true)
{
Penser;
region Etat
when Etat [(i-1) mod 5] <> M and Etat [(i+1) mod 5] <> M
do Etat [i] := M;
Manger;
21
instruction notée wait, qui met le processus en attente inconditionnelle. Cette instruction peut
se placer n'importe où à l'intérieure de la séquence s :
Region v do begin
i1 ;
wait ;
...
in ;
end ;
Les instructions placées avant wait sont exécutées en exclusion mutuelle. Ensuite, le
processus est sorti de l'exclusion mutuelle puis suspendu par wait. Lorsqu'il est relancé, il est
replacé en exclusion mutuelle, exécute les instructions qui suivent wait, puis sort de
l'exclusion mutuelle.
Region v do
begin
if not b wait ;
s;
end ;
22