Académique Documents
Professionnel Documents
Culture Documents
Chapitre III :
La concurrence et
synchronisation des processus
Amine DHRAIEF
ESEN, Univ. Manouba
Contexte: Synchronisation
●
Les relations entre processus peuvent prendre deux
formes :
– compétition pour l’usage d’une ressource partagée ;
– coopération pour l’exécution coordonnée d’une tâche
commune
●
L’exemple le plus courant de compétition est celui
de l’allocation du processeur.
– Une politique d’allocation attribue le processeur, par tranches
de temps successives, aux processus qui en ont besoin.
01/04/18 2
Contexte: Synchronisation
●
Un exemple simple de coopération est celui de la
transmission de messages entre deux processus à travers
une zone de mémoire commune (la boîte aux lettres) :
– si la boîte aux lettres est vide, le processus destinataire doit attendre
que le processus émetteur y dépose un message ;
– si la boîte aux lettres est pleine (c’est à- dire si l’on ne peut plus y
déposer de message sans détruire un de ceux qui s’y trouvent), le
processus émetteur doit attendre que le récepteur retire un message.
– Dans une situation intermédiaire où la boîte aux lettres n’est ni vide ni
entièrement remplie, les deux processus peuvent fonctionner en
parallèle à condition de ne pas accéder simultanément au même
message.
01/04/18 3
Contexte: Synchronisation
●
On appelle synchronisation la mise en œuvre des relations entre
processus en vue de respecter les contraintes de coopération.
●
L’exemple précédent montre que la synchronisation nécessite
de « faire attendre » un processus jusqu’à ce qu’une condition
de passage soit réalisée. Cela peut être obtenu de deux
manières :
– par attente active : on fait boucler le processus en attendant que la
condition soit réalisée ;
– par blocage : on fait passer le processus dans un état dit bloqué, dans
lequel il cesse de progresser ; lorsque la condition de passage est réalisée,
le processus est explicitement débloqué (ou réveillé), et reprend son
exécution.
01/04/18 4
Contexte: Synchronisation
●
Pour l’étude de la synchronisation :
– nous considérons l’exécution des processus d’un
point de vue purement logique
– sans tenir compte des vitesses réelles d’exécution.
●
De ce point de vue, il importe peu que
l’exécution se fasse en parallélisme réel ou
en pseudo-parallélisme
01/04/18 5
Contexte: Communication entre Processus
●
Le système d'exploitation gère les processus. Malgré cela,
chaque processus :
– Est une entité autonome et indépendante ;
– Vit isolé dans son propre espace mémoire ;
→ Par conséquent : Les processus peuvent se trouver en conflit pour
l'accès à certaines ressources communes
●
La solution :
→ Mécanisme de communication inter-processus : échange d'informations
entre les processus en vue de résoudre un problème
→ Nécessite de la mise en œuvre de mécanismes dits de synchronisation
pour gérer ces conflits.
01/04/18 6
Contexte: Communication entre Processus
01/04/18 7
SYNCHRONISATION ENTRE
PROCESSUS
01/04/2018 8
Exemple : spool d'impression
01/04/2018 9
Exemple : spool d'impression
Processus A
Spool
Démon
d’impressi d’impression
on
Processus B
01/04/2018 10
Exemple : spool d'impression
• Supposons que :
– Le répertoire d'impression ait un très grand nombre d'emplacements, numérotes
0,1,...,N. Chaque emplacement peut contenir le nom d'un fichier à imprimer;
– Tous les processus partagent la variable IN qui pointe sur le prochain emplacement
libre du répertoire ;
– Deux processus A et B veulent placer chacun un fichier dans la file d'impression et la
valeur de IN est 7 ;
– Le processus A place son fichier à la position IN qui vaut 7.
– Une interruption d'horloge arrive immédiatement après et le processus A est suspendu
pour laisser place au processus B;
– Ce dernier place également son fichier à la position IN qui vaut toujours 7 et met à jour
IN qui prend la valeur 8. Il efface ainsi le nom du fichier place par le processus A;
– Lorsque le processus A sera relancer, il met à jour la valeur de IN qui prend la valeur 9.
– Le fichier du processus A ne sera jamais imprimé;
– IN ne pointe plus sur le prochain emplacement libre.
01/04/2018 11
Problèmes et solution
01/04/2018 12
Définition
• Problème de synchronisation:
– Si un des processus impliqués dans l’accès en
parallèle à la ressource partagée modifie son
contenu
– Si les processus en accès simultanés sont tous
en mode consultation, il n'y a pas de problème
de synchronisation puisque la ressource
partagée n'est pas modifié.
01/04/2018 13
Définition
• Objet critique:
01/04/2018 14
Définition
• Section critique:
– Ensemble de suites d'instructions qui opèrent sur un ou
plusieurs objets critiques (partie de code devant être
exécutée de manière atomique)
– qui peuvent produire des résultats imprévisibles lorsqu'elles
sont exécutées simultanément par des processus différents.
01/04/2018 15
Conditions pour la survenue d'une exclusion mutuelle
●
Pour mettre en œuvre correctement l’accès exclusif à une section critique, il
faut s’assurer que les propriétés suivantes sont vérifiées.
●
Unicité :
– Un et un seul processus en secton critque
●
Pas d’excès de politesse : Conditon de Progression
– Si plusieurs processus sont en atente pour entrer dans leur secton critque, alors qu'aucun ne se trouve en
secton critque, l'un d'eux doit pouvoir y rentrer au bout d'un temps fni.
●
Non interblocage :
– Si un processus est bloqué hors sa secton critque, ce blocage ne doit pas empêcher les autres processus
d'entrer en secton critque.
●
Équité :
– Il n'existe aucun privilège entre les divers processus et l’entrée de la secton critque ne doit pas dépendre de
la volonté d’un processus partculier.
01/04/2018 16
Conditions pour la survenue d'une
exclusion mutuelle
01/04/2018 17
L’EXCLUSION MUTUELLE AVEC
ATTENTE ACTIVE
01/04/2018 18
L’exclusion mutuelle avec attente active
01/04/2018 19
Masquage d'interruption: solution naïve
• SOLUTION DANGEREUSE
– car le processus courant peut, pour diverses
raisons, ne pas réactiver les interruptions.
– La solution n'assure pas l'exclusion mutuelle.
01/04/2018 20
Les variables de verrouillage
01/04/2018 21
Algorithme de verrouillage
while verrou != 0 do
; // Attente active
end while
verrou = 1
Section_critique();
verrou = 0
01/04/2018 22
Algorithme de verrouillage
01/04/2018 23
Alternance stricte
01/04/2018 24
Exemple : alternance stricte
// Processus P1 // Processus P2
Section_critique() ; Section_critique() ;
tour = 1 ; tour = 0 ;
Section_noncritique() ; Section_noncritique() ;
... ...
} }
01/04/2018 25
Alternance stricte
01/04/2018 26
Problème de l’alternance stricte
01/04/2018 27
Algorithme de Peterson
• Combinaison de
– Intervention tour à tour des processus
– Variable de verrous
– Variables d’avertissement (warning variables)
01/04/2018 28
Algorithme de Peterson
#define FALSE 0
#define TRUE 1
#define N 2 /* number of processes */
int turn; /* whose turn is it? */
int interested[N]; /* all values initially 0 (FALSE)*/
01/04/2018 29
Algorithme de Peterson
• Combinaison de
– Intervention tour à tour des processus
– Variable de verrous
– Variables d’avertissement (warning variables)
01/04/2018 31
Algorithme de Peterson
01/04/2018 33
Conclusion sur les méthodes d'attente
active
• L’algorithme de Peterson apporte une solutions viable, mais
au prix d'attentes actives qui consomment du temps CPU !
01/04/2018 34
Conclusion sur les méthodes d'attente
active
●
Un autre problème est de vérifier qu'il y a un
minimum d’équité entre les deux processus,
c'est-a- dire que l'un d'entre eux ne peut pas
attendre indéfiniment son tour d'accès à la
section critique alors que l'autre y accède un
nombre infini de fois.
●
Cette question est plus difficile, de même
que la généralisation de la solution à un
nombre quelconque de processus.
01/04/2018 35
LE SOMMEIL ET L’ACTIVATION
01/04/2018 36
Sleep and Wakeup
01/04/2018 37
Sleep and Wakeup
●
Par exemple, un processus H qui veut entrer
dans sa section critique est suspendu si un
autre processus B est déjà dans sa section
critique.
●
Le processus H sera réveillé par le processus
B, lorsqu'il quitte la section critique.
01/04/2018 38
Problème du producteur-consommateur
01/04/2018 39
Problème du producteur-consommateur
●
Les problèmes se produisent lorsque le producteur
souhaite placer un nouvel élément dans le tampon alors
que ce dernier est déjà plein
●
La solution pour le producteur est d’entrer en sommeil,
pour être réveillé lorsque le consommateur aura supprimé
un ou plusieurs éléments du tampon
●
Si le consommateur souhaite récupérer un élément dans
le tampon et qu’il constate que celui-ci est vide, il entre en
sommeil jusqu’à ce que le producteur ait placé quelque
chose dans le tampon, opération qui va le réveiller
01/04/2018 40
Problème du producteur-consommateur
●
Pour effectuer le suivi du nombre d’éléments présent
dans le tampon, nous avons besoin d’une variable count.
●
Si le nombre maximal d’élément que le tampon peut
contenir est N :
– Le code du producteur commence par effectuer un test pour vérifier
que la valeur de count est N.
– Dans l’affirmative le producteur entre en sommeil, dans la
négative, il ajoute un élément au tampon et incrémente count.
– Le consommateur test si count vaut 0, si c’est le cas, il entre en
sommeil. Sinon, il récupère un élément et décrémente le compteur
01/04/2018 41
Problème du producteur-consommateur
#define N 100 /* number of slots in the buffer */
int count = 0; /* number of items in the buffer */
void producer(void)
{
int item;
while (TRUE){ /* repeat forever */
item = produce_item(); /*generate next item */
if (count == N) sleep(); /*if buffer is full, go to sleep*/
insert_item(item); /*put item in buffer */
count = count + 1; /*increment count of items in buffer */
if (count == 1) wakeup(consumer); /* was buffer empty? */
}
}
01/04/2018 42
Problème du producteur-consommateur
void consumer(void)
{
int item;
while (TRUE){ /* repeat forever */
if (count == 0) sleep(); /* if buffer is empty, got to
sleep */
item = remove_item(); /* take item out of buffer */
count = count - 1; /* decrement count of items in
buffer */
if (count == N - 1) wakeup(producer); /* was buffer
full? */
consume_item(item); /* print item */
}
}
01/04/2018 43
Problème du producteur-consommateur
01/04/2018 44
Problème du producteur-consommateur
01/04/2018 46
Edsger Dijkstra
• né à Rotterdam le 11 mai 1930 et mort à Nuenen le 6 août 2002, est un mathématicien et
informaticien néerlandais du XXe siècle
• Citations:
– « Tester un programme peut démontrer la présence de bugs, jamais leur absence. »
– « Se demander si un ordinateur peut penser est aussi intéressant que de se demander si un sous-marin
peut nager. »
– « La programmation par objets est une idée exceptionnellement mauvaise qui ne pouvait naître qu'en
Californie. »
– « Les progrès ne seront possibles que si nous pouvons réfléchir sur les programmes sans les imaginer
comme des morceaux de code exécutable. »
– « Autrefois les physiciens répétaient les expériences de leurs collègues pour se rassurer. Aujourd'hui ils
adhèrent à FORTRAN et s'échangent leurs programmes, bugs inclus. »
– « À propos des langages : il est impossible de tailler un crayon avec une hache émoussée. Il est vain
d'essayer, à la place, de le faire avec dix haches émoussées. »
– « Il est pratiquement impossible d'enseigner la bonne programmation aux étudiants qui ont eu une
exposition antérieure au BASIC : comme programmeurs potentiels, ils sont mentalement mutilés, au-delà
de tout espoir de régénération. »
– « Le plus court chemin d'un graphe n'est jamais celui que l'on croit, il peut surgir de nulle part, et la plupart
du temps, il n'existe pas. »
01/04/2018 47
LES SÉMAPHORES
• En 1965, E.W.Dijkstra suggère d’utiliser un nouveau
type de variable entière qui devait permettre de
décompter le nombre de wakeup enregistrer pour un
usage ultérieur
01/04/2018 48
LES SÉMAPHORES
●
Dijkstra proposait d’exploiter deux opérations, down et
up.
●
down (noté aussi P , P = Probeer ('Try')) sur un sémaphore
– détermine si sa valeur est supérieur à 0. Si c’est le cas, elle la décrémente et
poursuit son activité.
01/04/2018 49
LES SÉMAPHORES
●
Il est garanti qu’une fois un sémaphore a
démarré, aucun autre processus ne peut y
accéder tant que l’opération n’est pas
terminé ou bloquée.
●
Cette atomicité est essentiel à la résolution
des problèmes de synchronisation ainsi
pour éviter les conditions de concurrence.
01/04/2018 50
LES SÉMAPHORES
●
L’opération up (noté aussi V, V = Verhoog ('Increment', 'Increase by
one')) incrémente la valeur du sémaphore concerné
– Si un ou plusieurs processus se trouvaient en sommeil sur ce
sémaphore, incapable de terminer une opération down antérieure, l’un
d’eux est choisi par le système et est autorisé à terminé son down
01/04/2018 51
LES SÉMAPHORES
●
La méthode habituelle consiste à
implémenter les appels up et down en
tant qu’appels systèmes
●
Le système désactive toutes les
interruptions pendant un très court laps
de temps durant lequel il teste le
sémaphore, l’actualise et place si
nécessaire le processus en sommeil
01/04/2018 52
Résolution du problème producteur-
consommateur avec des sémaphores
• On utilise trois sémaphores
01/04/2018 54
Résolution du problème producteur-
consommateur avec des sémaphores
#define N 100 /* number of slots in the buffer */
typedef int semaphore; /* semaphores are a special kind of
int */
semaphore mutex = 1; /* controls access to critical region
*/
semaphore empty = N; /* counts empty buffer slots */
semaphore full = 0; /* counts full buffer slots */
01/04/2018 55
Résolution du problème producteur-
consommateur avec des sémaphores
void producer(void)
{
int item;
while (TRUE){ /* TRUE is the constant 1 */
item = produce_item(); /* generate something to put
in buffer */
down(&empty); /* decrement empty count */
down(&mutex); /* enter critical region */
insert_item(item); /* put new item in buffer */
up(&mutex); /* leave critical region */
up(&full); /* increment count of full slots */
}
}
01/04/2018 56
Résolution du problème producteur-
consommateur avec des sémaphores
void consumer(void)
{
int item;
while (TRUE){ /* infinite loop */
down(&full); /* decrement full count */
down(&mutex); /* enter critical region */
item = remove_item(); /* take item from buffer */
up(&mutex); /* leave critical region */
up(&empty); /* increment count of empty slots */
consume_item(item); /* do something with the item */
}
}
01/04/2018 57
Tableau de Jean Huber (1721-1786),
le Dîner des philosophes
Le dîner des philosophes
01/04/2018 72
Le problème des lecteurs et des
rédacteur, priorité aux lecteurs !
Le problème des lecteurs et des
rédacteur
• Supposons que, pendant qu’un lecteur utilise
la base, un autre lecteur se connecte. Étant
donné que deux lecteurs peuvent consulter la
base en même temps, le deuxième lecteur est
admis. Un troisième lecteur, et d’autre le seront
également s‘ils se présentent.
01/04/2018 76
Les mutex
●
Quand des décomptes ne sont pas
nécessaire, on utilise une version simplifiée
des sémaphores qu’on appelle des mutex.
– Ils prennent en charge l’exclusion mutuelle
●
Un mutex est une variable qui peut prendre
deux états: déverrouillée ou verrouillée
– Un seul bit est nécessaire pour la représenter
Les mutex
●
Deux procédure interviennent avec les mutex
– mutex_lock / mutex_unlock
●
Lorsqu’un thread (processus) a besoin d’accéder à sa
section critique, il invoque mutex_lock.
– Si le mutex est déverrouillé (la section critique est donc
disponible), l’appel réussit et le thread appelant est libre d’entrer
en section critique.
– Si le mutex est déjà verrouillé, le thread appelant est bloqué
jusqu’à ce que le thread en section critique en ait terminé et
appel mutex_unlock.
– Si plusieurs threads sont bloqués sur le mutex, l’un d’eux est
choisi aléatoirement pour prendre possession du verrou.
Les mutex dans pthreads
●
Le thread qui désire entrer dans une région critique
doit tout d’abord verrouiller le mutex associé.
●
Si ce mutex est déverrouillé, le thread peut
immédiatement entrer et le verrouillage est fait
automatiquement, ce qui empêche d’autres threads
d’entrer.
●
Si le mutex est déjà verrouillé, le thread appelant
est bloqué jusqu'au déverrouillage.
Les mutex dans pthreads
Appel Description
pthread_mutex_init Crée un nouveau mutex
●
Pthreads offre également un mécanisme de
synchronisation autre les mutex, les
variables de condition,
– Ils permettent à un thread de se bloquer non pas à
l’entrée d’un région critique mais en fonction
d’une certaine condition
●
Les mutex et les variables de condition sont
utilisés conjointement
Les mutex dans pthreads
●
Si on considère le scénario de
producteur-consommateur :
– Un thread remplit un tampon, un autre le vide
– Si le producteur découvre que le tampon est plein, il
doit se bloquer jusqu'à ce qu'un emplacement se libère
– Les mutex permettent de faire ce test atomiquement,
ce qui conduira un thread à se bloquer si le tampon est
plein
→ C'est une variable de condition qui permettra de
le réactiver
Les mutex dans pthreads
Appel Description
●
Les raisons pour lesquelles on bloque ou on débloque un
thread sont externes :
– Un thread bloqué attend souvent qu'une ressource se libère ou que soit
effectuée une certaine tâche
– La variable de condition signalera que cela est fait
●
Il faut noter que les variables de
condition, à la différence des
sémaphores sont sans mémoire.
– Si on envoie un signal à une variable de conditon sur
laquelle il n'y a aucun thread en atente, le signal est perdu
Les mutex dans pthreads
●
Les variables conditions sont représentées par le type
pthread_cond_t.
●
On initialise généralement les conditions de manière
statique
pthread_cond_t condition = PTHREAD_COND_INITIALIZER;
●
On peut aussi employer la fonction pthread_cond_init( ),
en passant un second argument NULL
int pthread_cond_init (pthread_cond_t * condition,
pthread_condattr_t * attributs);
Les mutex dans pthreads
●
L'initialisation statique d'un mutex se fait à l'aide
de la constante
PTHREAD_MUTEX_INITIALIZER
● pthread_mutex_t mutex =
PHTREAD_MUTEX_INITIALIZER;
Exemple 1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
}
THE END
01/04/2018 100