View
1
Download
0
Category
Preview:
Citation preview
Systèmes d’Exploitation
chapitre 7
Moniteurs
F. Moal
2012/2013
2
Moniteurs
Gestion des contraintes de synchronisation
et d’exclusion mutuelle
par : BRINCH HANSEN, HOARE 1973
But : décharger le programmeur de la gestion explicite de
l’exclusion mutuelle
3
Moniteurs
Idée de départ: Encapsulation
Var r: ressource
Process p;
…
with r do <section critique>
…
with r do <section critique>
…
End p;
4
Moniteurs
Moniteur : type abstrait de données [classe]
Encapsulation
- Variables
- Opérations [méthodes]
- Conditions
Condition : évènement dont on attend la réalisation
Ex: tampon_vide / tampon_plein
5
Moniteurs
A chaque condition C d’un moniteur sont
associées 3 opérations :
C.attendre [C.wait] : met en attente le processus
qui l’exécute (une file d’attente par condition)
C.vide : faux s’il existe un ou plusieurs
processus en attente de C, vrai sinon
C.signaler [C.signal] : réveille un unique
processus en attente de C, s’il existe.
6
Moniteurs: spécification
La spécification des moniteurs fournit
gratuitement l’exclusion mutuelle
Règle 1 : les variables du moniteur ne sont
accessibles QUE par l’intermédiaire des
procédures et fonctions du moniteur [=variables
privées]
Règle 2 : le moniteur entier est implanté comme
une section critique
7
Moniteurs: spécification
La spécification des moniteurs fournit gratuitement
l’exclusion mutuelle
A un instant donné, au plus UN processus peut être à
l’intérieur du moniteur c-à-d en train d’exécuter une
procédure ou fonction du moniteur.
Lorsqu’un processus demande l’exécution d’une procédure
P du moniteur, 2 possibilités :
1. Aucun autre processus n’est déjà à l’intérieur du moniteur : la
procédure est exécutée
2. Un processus exécute déjà une des procédures ou fonctions du
moniteur : le processus appelant est mis en attente du moniteur et
sera réveillé lorsque le moniteur redeviendra disponible
8
Moniteurs: utilisation
Idée : déclarer toutes les variables
partagées dans un moniteur
Sémaphore de synchronisation condition
Sémaphore d’exclusion mutuelle disparition !
Chaque exécution d’une procédure du moniteur
est une opération atomique
9
Moniteurs: utilisation
Exemple 1 :
P produit a
fin(P) ≤ début(Q)
Q utilise a
synchro : Moniteur
fait : booléen
fini : condition
Procédure fin_production
fait = vrai
fini.signaler
Procédure début_utilisation
si (!fait) alors fini.attendre
Initialisation
fait = faux
P Q
fait
10
Moniteurs: utilisation
synchro : Moniteur
fait : booléen
fini : condition
Procédure fin_production
fait = vrai
fini.signaler
Procédure début_utilisation
si (!fait) alors fini.attendre
Initialisation
fait = faux
P :: produire(objet) Q :: synchro.début_utilisation()
synchro.fin_production() utiliser(objet)
Rem : en ADA, moniteur = type protected
en Java : synchronised (cf plus loin)
P Q
fait
11
Moniteurs: utilisation
Exemple 2 : Rendez-vous
N processus se donnent Rendez-vous en un point donné
: avant de continuer leurs exécutions, ils doivent
attendre que TOUS soient arrivés au pt de Rendez-
vous.
Idée :
Quand un processus arrive au point de RV, il
s’enregistre (n=n+1)
tant que n<N [tous là] attendre
12
Moniteurs: utilisation
RV : Moniteur
n : entier
touslà : condition
Procédure arrivée
n = n +1
si (n<N) alors touslà.attendre
sinon touslà.signaler
Initialisation
n = 0
13
Moniteurs: utilisation
Exemple 3 : Gestion de ressources banalisées
Par ex, tampons d’Entrée/Sortie
A chaque fois qu’un processus lit un fichier, un tampon
d’E/S lui est alloué
Chaque process a besoin d’autant de tampons que de
fichiers ouverts
Il y a pool fini de tampons dans le système : variable
partagée nlibre
Demander(n) pour se voir allouer n tampons
Libérer(n) pour remettre les tampons dans le pool
14
Moniteurs: utilisation
ressource : Moniteur
nlibre : entier
dispo : condition
Procédure demander(n)
si (n>nlibre) alors disp.attendre
nlibre = nlibre -n
Procédure liberer(n)
nlibre = nlibre + n
disp.signaler
Initialisation
nlibre = N
15
Moniteurs: utilisation
Pb1
au moment du réveil, le processus réveilleur (celui qui fait signaler) ne peut pas garantir que la condition attendre est devenue vraie !
Donc il faut faire un tant que au lieu d’un si : l’attente doit être conditionnée par un tant que
Pb2
pour avoir un réveil en chaîne, ajouter dispo.signaler
16
Moniteurs: utilisation
Modification du moniteur :
Gérer une file des demandes des processus
Associer une condition à chaque processus
pour pouvoir effectuer un réveil sélectif
17
Moniteurs: utilisation
Exemple 4 : Exclusion mutuelle
placer l’indicateur occupé dans un moniteur pour éviter
les conflits de lecture/écriture
mutex : Moniteur
occupé : booléen
libre : condition
Procédure entrer
si occupé alors libre.attendre
occupé = vrai
Procédure sortir
occupé = faux
libre.signaler
Initialisation
occupé = faux
18
Moniteurs: utilisation
Simulation section critique :
P mutex.entrer
SC SC
V mutex.sortir
19
Moniteurs: utilisation
Exemple 5 : Problème des lecteurs/rédacteurs
un livre [fichier] partagé par deux classes de processus :
Lecteurs (R)
Rédacteurs/écrivains (W)
Contraintes de synchronisation : à un instant donné,
1 rédacteur et 0 lecteur
Ou 0 rédacteur et un nb quelconque de lecteurs
20
Moniteurs: utilisation
Variables partagées :
nl : nombre de lecteurs
ecr : vrai si écriture en cours
à encapsuler dans un moniteur
C_ecr : attente si ecr ou nl > 0
C_lect : attente si ecr
Procédures :
debut_lire
fin_lire
début_écrire
fin_écrire
21
Moniteurs: utilisation
Exemple 6 : Producteurs/consommateur
Tampon de taille N fixée
Vide ?
Plein ?
22
Moniteurs: ordre d’accès
Exécution de C.attendre à l’intérieur du moniteur, en
section critique : blocage inutile du moniteur ?
Pour ne pas le bloquer, le processus exécutant le
C.attendre est sorti du moniteur section critique
libérée
Exécution de C.signaler par P dans le moniteur qui
réveille un processus Q en attente de C : 2
processus dans le moniteur !
le reveillé Q reprend son exécution dans le
moniteur, le réveilleur P est mis en attente du
moniteur
Types de moniteurs
Il existe en fait deux types de moniteurs :
De Hoare, ceux qui viennent d’être
étudiés,
De Kessel :
Plus de Condition ni de signaler !
Juste une intruction : wait(condition
booléenne) sur les variables du moniteur
Implantés en ADA 23
24
Moniteurs en Java
Primitive de base Java pour la synchronisation:
synchronized
wait
notify / notifyAll
Règle : un moniteur est associé à CHAQUE INSTANCE d’une classe qui possède au moins une méthode du type synchronised
Java associe à chaque objet un cadenas (lock) qui sert à contrôler l’accès des threads à cet objet
25
Moniteurs en Java : pour threads !!!
2 façons de créer des threads Java :
1) créer une instance d’une classe fille de la
classe Thread ; la classe fille doit redéfinir
la méthode run()
2) utiliser le constructeur Thread(Runnable)
de la classe Thread :
1. créer un Runnable (le code qui sera contrôlé
par le contrôleur)
2. le passer au constructeur de Thread
26
class ThreadTache extends Thread {
. . .
public void run() {
// Code qui sera exécuté par le thread
. . .
}
}
...
ThreadTache threadTache = new
ThreadTache(…);
Threads : 1) par héritage
27
class Tache implements Runnable {
. . .
public void run() {
// Code qui sera exécuté par le thread
. . .
}
}
...
Tache tache = new Tache(…);
Thread t = new Thread(tache) ;
Threads : 2) par Interface
28
On appelle la méthode start() du contrôleur
de thread :
t.start();
Le code du Runnable s’exécute en parallèle
au code qui a lancé le thread
Attention ! une erreur serait d’appeler
directement la méthode run() : la méthode
run() serait exécutée par le thread qui l’a
appelée et pas par un nouveau thread
Threads : lancement
29
Moniteurs en Java : synchronized
synchronized Lorsqu’un thread rencontre la primitive
synchronized, il obtient le cadenas sur l’objet et bloque tout accès à celui-ci aux autres threads qui rencontreraient une primitive synchronized
deux utilisations possibles:
1. Comme modificateur de méthode
2. dans le code afin d’obtenir un lock sur une ressource
30
Moniteurs en Java : synchronized
1. Comme modificateur de méthode
Ceci indique alors que la méthode entière est une
section critique pour une classe
public synchronized int get() {
// section critique }
Une fois qu’un thread entre et exécute la méthode get() de l’objet, elle obtient le cadenas sur l’objet et toute tentative d’accès par un autre thread à travers une des méthodes synchronized de l’objet est alors mise en attente.
31
Moniteurs en Java : synchronized
2. dans le code afin d’obtenir un lock sur une
ressource
public void methode() {
…
synchronized (obj) {
// section critique
}
…
}
Cela permet une granularité plus fine des locks
32
Moniteurs en Java : wait / notifyAll
wait et notify/notifyAll
Dans une section critique pour libérer temporairement le cadenas qu’il possède sur un objet, un thread peut invoquer wait. Mise à part la libération du lock qui signifie qu’un autre thread peut maintenant obtenir le lock, wait a pour conséquence de mettre en attente le thread qui l’a invoqué.
Et pour réveiller des threads qui sont en attente du cadenas d’un objet, on utilisera la primitive notify (1) ou notifyAll (tous).
33
Moniteurs en Java
Exemple : Producteur / Consommateur
3 classes :
- Tampon : moniteur de synchronisation, avec
deux méthodes : put dépose et get récupère
- Producteur : thread
- Consommateur : thread
- Plus une classe de test qui lance les threads
34
Moniteurs en Java
class Tampon {
// tampon de stockage des objets produits
private static final int TAILLE = 10;
private Object [] tampon = new Object[TAILLE];
// nbre d'objets dans le tampon
private int n = 0;
...
35
Moniteurs en Java
// recupere un produit dans le tampon
public synchronized Object get(){
// tant qu’il n’y a pas de produit,
// s’endormir et attendre un réveil
while( n == 0 ){
System.out.println("dort sur get");
try {
wait();
} catch(InterruptedException e) {}
System.out.println("réveil sur get");
}
Object objet = tampon[n-1];
n = n-1;
// réveille le producteur pour qu’il produise
notifyAll();
return objet;
}
36
Moniteurs en Java
// stocke un produit dans le tampon
public synchronized void put(Object o){
// tant que le tampon est plein,
s’endormir
while( n == TAILLE-1 ){
try {
wait();
} catch(InterruptedException e) {}
}
n = n+1;
tampon[n-1] = o;
// réveil du(des) consommateur(s)
notifyAll();
}
}
37
Moniteurs en Java
public class Producteur extends Thread {
private Tampon tampon;
private int numero;
public Producteur(Tampon t, int no) {
tampon = t;
numero = no;
}
public void run() {
for(int i=0; i<100; i++) {
tampon.put(new Integer(i));
try {
sleep( (int)Math.random()*1000 );
} catch (InterruptedException e) {}
}
}
} // end of class Producteur
38
Moniteurs en Java
public class Consommateur extends Thread {
private Tampon tampon;
private int numero;
public Consommateur(Tampon t, int no) {
tampon = t;
numero = no;
}
public void run() {
for(int i=0; i<100; i++) {
Integer val = (Integer)tampon.get();
try {
sleep( (int)Math.random()*100 );
} catch (InterruptedException e) {}
}
}
} // end of class Consommateur
39
Moniteurs en Java
public class TestProducteurConsommateur {
public static void main(String[] args) {
// un seul tampon pour tous les threads
Tampon t = new Tampon();
// création des threads prod/consommateurs
// avec des n° pour les différentier
Producteur p1 = new Producteur(t,1);
Consommateur c1 = new Consommateur(t,1);
Consommateur c2 = new Consommateur(t,2);
// lancement des threads
p1.start();
c1.start();
c2.start();
}
} // end of class TestProducteurConsommateur
40
Moniteurs en Java : java 5
utiliser un seul verrou, mais avec plusieurs variables condition :
class UneClasse {
private final Lock lock = new ReentrantLock();
private final Condition cond1 = lock.newCondition();
private final Condition cond2 = lock.newCondition();
...
41
Moniteurs en Java : java 5
...void methode1() throws InterruptedException {
lock.lock();
try {
cond1.await();
cond2.signal();
} finally {
lock.unlock();
}
}
void methode2() throws InterruptedException {
lock.lock();
try {
cond1.signal();
cond2.await();
} finally {
lock.unlock();
}
}
42
Moniteurs en Java : java 5
Introduction d’une classe Sémaphore :
Semaphore sem = new Semaphore(1);
try {
sem.acquire();
//section critique
sem.release();
} catch(InterruptedException e) {
e.printStackTrace();
}
43
Moniteurs en Java : java 5/6
Le package java.util.concurrent (ainsi que ses sous-packages) fournissent des outils de synchronisation de plus haut niveau.
Par exemple, fournit une liste bloquante, implanté par BlockingQueue (liste bloquante non bornée) et ArrayBlockingQueue (liste bloquante à tampon borné).
Cf : http://docs.oracle.com/javase/tutorial/essential/concurrency/
Recommended