1
Systèmes d’exploitation
Chap. 5
La concurrence entre processus
2
1. Introduction
La communication entre processus : Trois problématiques
• Le passage d’informations
• Pas de conflits dans les sections critiques
( deux processus tentent de récupérer le dernier Mo de mémoire)
• Séquençage entre deux processus
( le processus A produit des données, B les imprime)
Chap. 5 - Concurrence
3
1.1 Les conditions de concurrence
Dans certains systèmes d’exploitation, les processus peuvent partager un espace de stockage commun
• Variable en mémoire centrale
• Un fichier
L’emplacement de la mémoire partagée ne modifie pas les problèmes qui peuvent survenir.
Proc. 1 Proc. 2 N processus ont
accès à une même donnée
Chap. 5 - Concurrence
4
1.1 Les conditions de concurrence
Exemple 1 : débit d’un compte bancaire
Retire 100€
Virement 100€
Relevé de compte
Solde : 1000€
Solde : 900€ !!!
Vérification solde
Vérification solde
1000€ – 100€ = 900€
1000€ – 100€ = 900€
Chap. 5 - Concurrence
5
1.1 Les conditions de concurrence
Exemple 2 : le spouleur d’impression
Démon d’impression
In : prochain fichier libre du répertoire Out : prochain fichier à imprimer
Proc. 1
Proc. 2
In = 3 Out = 0 P2.cP1.c
0 1 2 3 4
Toto.txt File.c Test.pdf
Chap. 5 - Concurrence
6
Sections critiques
1.2 Les sections critiques
Comment éviter les conditions de concurrence ?
• Le problème est définie par « les sections critiques »
• Trouver des mécanismes d’exclusion mutuelle
( si un processus utilise une variable, les autres seront exclus de la même activité. )
Variable A
Processus P1
…
Lecture(A) A++
Ecriture(A)
…
Processus P2
…
Lecture(A) A--
Ecriture(A)
…
Chap. 5 - Concurrence
7
1.2 Les sections critiques
Quatre conditions doivent être respectées :
1. Deux processus ne doivent pas se trouver simultanément dans leurs sections critiques.
2. Il ne faut pas faire de suppositions quant à la vitesse ou au nombre de processus mis en œuvre.
3. Aucun processus s’exécutant à l’extérieur de sa section critique ne doit être bloquer par d’autres processus.
4. Aucun processus ne doit attendre indéfiniment pour pouvoir entrer dans sa section critique.
Chap. 5 - Concurrence
8
1.2 Les sections critiques
Processus A
Processus B
B est bloqué A entre dans sa
section critique
A quitte sa section critique
B tente d’entrer dans sa section critique
B entre dans sa section critique
B quitte sa section critique
T1 T2 T3 T4
Chap. 5 - Concurrence
9
2. Exclusion mutuelle avec attente active
Comment obtenir l’« exclusion mutuelle » ?
• Désactivation des interruptions
• Variable verrou
• Alternance strict (spin lock)
• Solution de Peterson
• Instruction TSL
• Primitive sleep et wakeup Chap. 5 - Concurrence
10
2.1 Désactivation des interruptions
IRQ (de l'anglais Interrupt Request) une interruption est déclenchée par une ligne d'entrée-sortie matérielle d'un microprocesseur ou d'un microcontrôleur.
Les IRQ sur les premiers IBM PC/XT étaient définies comme suit (par ordre de priorité décroissante) :
IRQ 0 : Horloge système IRQ 1 : Clavier
IRQ 2 : Second contrôleur d'interruption (PC/AT) IRQ 3 : Port série (COM2/COM4)
IRQ 4 : Port série (COM1/COM3) IRQ 5 : Disque dur
IRQ 6 : Lecteur de disquettes IRQ 7 : Port parallèle (LPT1)
Chap. 5 - Concurrence
11
2.1 Désactivation des interruptions
Solution naïve :
Tous les processus désactivent les interruptions - processus utilisateurs
- processus systèmes
Le processeur ne peut pas basculer d’un processus à l’autre que s’il reçoit des interruptions d’horloge ou autre.
Problèmes :
Que se passe-t-il s’il ne peut pas les réactiver ? Chap. 5 - Concurrence
12
Variable lock Partagée par tout
les processus utilisateurs
2.2 Variable verrou
Le verrou est une solution logicielle :
Deux états : 0 et 1 Initialement à 0
Lock = 1
Zzzz Zzzz Zzzz
Lock = 0
Ce n’est pas une solution !!
Chap. 5 - Concurrence
13
Variable lock Partagée par tout
les processus utilisateurs
2.2 Variable verrou
Le verrou est une solution logicielle :
Deux états : 0 et 1 Initialement à 0
Lock = 1
Il existe toujours une section critique
Chap. 5 - Concurrence
14
2.3 Alternance strict (spin lock)
Variable turn Partagée par tout
les processus utilisateurs Valeur entre : 0 et n
Chap. 5 - Concurrence
15
2.3 Alternance strict (spin lock)
Attente active des deux processus
while(TRUE){
while (turn != 0); // loop section_critique();
turn = 1;
section_non_critique();
}
Variable turn
while(TRUE){
while (turn != 1); // loop section_critique();
turn = 0;
section_non_critique();
}
Processsus bloqué hors section critique, si l’un est plus rapide que l’autre
Chap. 5 - Concurrence
16
2.4 Solution de Peterson
#define FALSE 0
#define TRUE 1
#define N 2 int turn;
int interested[N];
void entrer_region( int process) { int other;
other = 1 – process;
interested [ process ] = TRUE;
turn = process;
while (turn == process && interested[other] == TRUE) }
void sortir_region( int process) {
interested[process] = FALSE;
}
interestedTRUE
// Si process == 1 alors other == 0 turn
// process == 1
1
// turn = 1
Chap. 5 - Concurrence
17
void entrer_region( int process) { … } void sortir_region( int process) { … }
2.4 Solution de Peterson
void entrer_region( int process) { … } void sortir_region( int process) { … }
interestedTRUE turn01 TRUE
Process 0 Zzz
Processus 1 Zzz
Chap. 5 - Concurrence
18
2.5 Instruction TSL
Instruction : TSL RX, LOCK
• Test and Set Lock
• Réaliser par le processeur
• Un seul processus exécute l’instruction (indivisible)
• Fonctionnement : lit « lock » à l’adresse contenu dans le registre RX et y place une valeur différent de zéro.
• Système de verrou
Variable lock Partagée par tout
les processus utilisateurs
Chap. 5 - Concurrence
19
2.5 Instruction TSL
Instruction : TSL RX, LOCK enter_section:
TSL REGISTER,LOCK ; copie lock dans reg. et le def à 1
CMP REGISTER,#1 ; lock était-il à 0 ?
JNE enter_section ; si elle ne l’était pas à 0, boucle
RET ; retourne à l’appelant
Leave_section:
MOVE LOCK,#0 ; stocke un 0 dans lock
Chap. 5 - Concurrence
20
2.6 Primitive sleep et wakeup
Les solutions de Peterson et de l’instruction TSL sont bonnes, mais elles font appel à l’« attente active ».
Consommatrice de temps processeur.
Solution mettre en sommeil les processus et ensuite les activer une fois la section critique terminée.
La plus simple manière de bloquer les processus au lieu de
comsommé du temps processeur fonctionne avec la paire sleep et wakeup.
Chap. 5 - Concurrence
21
2.6 Primitive sleep et wakeup
Les primitives « sleep » et « wakeup » sont deux appels systèmes.
La primitive « sleep » endort le processus qui l’appelle, en rendant la main à l’ordonnanceur.
La primitive « wakeup » permet de réveiller un processus endormi dont l’identité lui est passée en paramètre.
Ces paramètres sont utilisés dans des problématiques de producteur- consommateur.
Chap. 5 - Concurrence
22
2.6 Primitive sleep et wakeup
void producteur() { while (1){
producteur_objet(o);
if (compteur == N) sleep();
mettre_objet(o);
compteur++;
if (compteur == 1)
wakeup(consommateur);
} }
void consommateur() { while (1){
if (compteur == 0) sleep();
retirer_objet(o);
compteur--;
if (compteur == (N-1)) wakeup(producteur);
consommer_objet(o);
} }
#define N 100 int compteur = 0;
//file de 100 objets
// file pleine
// file vide
// file pleine // file vide
Chap. 5 - Concurrence
23
File vide
Consommateur Producteur
2.6 Primitive sleep et wakeup
Solution élégante mais conduit aux mêmes problèmes de conflits d’accès que pour l’exemple du « spouleur ».
Causer par l’accès concurrent à la variable compteur.
Lecture de la variable compteur == 0 par les
deux processus
Zzz
N’a pas eu le temps
Wakeup !!!
Wakeup perdu, car le consommateur ne Zzz… Zzz…
Dead lock
Système bloqué
Chap. 5 - Concurrence
24
3. Les sémaphores
Principe : utilisation d’un nouveau type de variable qui va compter le nombre de wakeup (1965, E. W. Dijkstra).
Le sémaphore peut avoir une valeur signifiant qu’aucun wakeup n’a été émis.
Les activités de vérification, de modification et éventuellement de
« mise en sommeil » sont toutes effectuées dans le cadre d’une
« action atomique » unique et indivisible.
Il est garanti que, une fois qu’un « sémaphore a démarré », aucun autre processus ne peut y accéder tant que l’opération n’est pas terminée ou bloquée.
Chap. 5 - Concurrence
25
3. Les sémaphores
Deux appels systèmes :
up : incrémente la valeur du sémaphore ~ wakeup down : décrémente la valeur du sémaphore ~ sleep
Les sémaphores résolvent le problème de perte des «wakeup»
Semaphore s;
down(s) { up(s) {
while (s == 0) ; /* wait until s>0 */ s = s + 1;
s = s - 1; }
Chap. 5 - Concurrence
26
3. Les sémaphores
#define N 100
typedef int semaphore;
semaphore mutex = 1;
semaphore plein = N;
semaphore vide = 0;
//file de 100 objets
void producteur() { while (1){
producteur_objet(o);
down(plein);
down(mutex);
inserer_objet(o);
up(mutex);
up(vide);
} }
void consommateur() { while (1){
down(vide);
down(mutex);
prendre_objet(o);
up(mutex);
up(plein);
consommer_objet(o);
} }
Chap. 5 - Concurrence
27
4. Les moniteurs
L’utilisation des sémaphores est délicate (Ne pas inverser le « down » des deux « mutex », risque d’interblocage)
Les moniteurs sont intégrés dans le langage lui-même.
Principes : un moniteur est une collection de procédures, de variables, de structures de données
• Les processus peuvent appeler les procédures à l’importe quel moment, mais pas d’accès direct aux données internes.
• Accès aux données à travers des procédures du moniteur.
Chap. 5 - Concurrence
28
4. Les moniteurs
A un instant donné, un seul
processus peut être « actif » dans le moniteur.
Les moniteurs font parti du langage, donc pas de risque d’oubli comme pour les sémaphores. (synchronized en Java)
Chap. 5 - Concurrence
29
4. Les moniteurs
Les moniteurs sont efficaces pour les exclusions mutuelles, mais pas pour les interblocages.
Introduction de variables conditionnelles : « wait » et « signal ».
Ex: File pleine alors le moniteur fait un « wait » sur le processus par rapport la variable conditionnelle « pleine ».
Après un « wait » sur un processus un autre prend sa place dans le moniteur.
Un autre processus peut réveiller un autre grâce à un « signal ».
Chap. 5 - Concurrence
30
Conclusion
Trois problématique :
Conditions de concurrence Section Critique
Exclusion mutuelle Interblocage
Exemple classique: le dîné de philosophes
Chap. 5 - Concurrence
31
• 5 philosophes qui mangent ou pensent
• Pour manger il faut 2 fourchettes, droite et gauche
• Seulement 5 fourchettes !
• Illustre la difficulté d’allouer des ressources aux processus tout en évitant « interblocage » et
« famine »
Exemple classique: le dîné de philosophes
Chap. 5 - Concurrence
32
« Philosophes mangeant » structures de données
• Chaque philos. a son propre « state » qui peut être (thinking, hungry, eating)
– philosophe i peut faire state[i] = eating ssi les voisins ne mangent pas
• Chaque processus a sa propre état « self »
– le philosophe i peut attendre sur self [ i ] s’il veut manger, mais il obtenir les 2 baguettes en même temps.
Chap. 5 - Concurrence
33
Chaque philosophe exécute à jamais:
repeat pickup eat
putdown forever
Chap. 5 - Concurrence
34
private test(int i) {
if ((state[i] == HUNGRY) &&
(state[(i + 4) % 5] != EATING) &&
(state[(i + 1) % 5] != EATING) ) { state[i] = EATING;
self[i].signal;
} }
Un philosophe mange
Un philosophe mange si ses voisins ne mangent pas et s’il a faim.
Une fois mangé, il signale de façon qu’un autre pickup soit possible, si pickup s’était arrêté sur wait
Il peut aussi sortir sans avoir mangé si le test est faux
Chap. 5 - Concurrence
35
Chercher à prendre les baguettes
public entry pickUp(int i) {
state[i] = HUNGRY;
test(i);
if (state[i] != EATING) self[i].wait;
}
Phil. cherche à manger en testant, s’il sort de test qu’il n’est pas mangeant il attend – un autre pickup Chap. 5 - Concurrence
36
Déposer les baguettes
public entry putDown(int i) { state[i] = Thinking;
// tester les deux voisins
test((i + 4) % 5);
test((i + 1) % 5);
}
Une fois fini de manger, un philosophe se préoccupe de faire manger ses voisins en les testantChap. 5 - Concurrence