Sémaphores Plan Plan

Sémaphores
Pierre David
[email protected]
Université de Strasbourg – Licence d’informatique
2014 – 2015
Plan
Principes
Implémentation
Sémaphores IPC System V
Sémaphores POSIX
Problèmes classiques de synchronisation
2
c
Pierre
David, [email protected]
Plan
Principes
Implémentation
Sémaphores IPC System V
Sémaphores POSIX
Problèmes classiques de synchronisation
3
c
Pierre
David, [email protected]
Principes
Verrou d’exclusion mutuelle (mutex) : mécanisme primitif de
synchronisation
Mécanisme de plus haut niveau : sémaphore de Dijkstra
I
I
un compteur
3 opérations atomiques
I
I
I
initialisation
P
V
c
Pierre
David, [email protected]
4
Principes
I
Opération atomique P :
P ( cpteur ) {
while ( cpteur <= 0)
;
/* attente active */
cpteur - }
P = proberen (tester) en néerlandais
Moyen mnémotechnique : P = « Puis-je ? »
I
Opération atomique V :
V ( cpteur ) {
cpteur ++ ;
}
V = verhogen (incrémenter) en néerlandais
Moyen mnémotechnique : V = « Vas-y ! »
c
Pierre
David, [email protected]
5
Principes
Compteur = nombre d’exemplaires disponibles d’une ressource
Exemple :
P
P(pk)
V(pk)
pk = 4
I
I
I
I
initialement, le compteur est initialisé à 12
il reste 4 exemplaires disponibles de la ressource « place
de parking »
barrière d’entrée = P ⇒ teste et décrémente le compteur
barrière de sortie = V ⇒ incrémente le compteur
6
c
Pierre
David, [email protected]
Principes
Deux cas principaux :
I
sémaphore général : le compteur peut prendre n’importe
quelle valeur
I
sémaphore binaire : le compteur peut prendre la valeur 0
ou la valeur 1
⇒ équivalent à un verrou d’exclusion mutuelle (mutex)
c
Pierre
David, [email protected]
7
Exemple
Soit les deux processus A et B :
A
B
i1
i2
On veut que i1 soit exécutée avant i2 :
A
B
i1
V(s)
P(s)
i2
Sémaphore s initialisé à 0
8
c
Pierre
David, [email protected]
Plan
Principes
Implémentation
Sémaphores IPC System V
Sémaphores POSIX
Problèmes classiques de synchronisation
9
c
Pierre
David, [email protected]
Implémentation naïve
Implémentation possible avec des spin mutexes :
verrou v1 , v2 ;
P ( cpteur ) {
spin_lock ( v1 ) ;
while ( cpteur <= 0)
;
/* attente active */
spin_lock ( v2 ) ;
cpteur - - ;
spin_unlock ( v2 ) ;
spin_unlock ( v1 ) ;
}
V ( cpteur ) {
spin_lock ( v2 ) ;
cpteur ++ ;
spin_unlock ( v2 ) ;
}
⇒ attente active + spin mutexes (attente active également)
10
c
Pierre
David, [email protected]
Implémentation avec attente passive
Attente passive ⇒ support du système d’exploitation
Modification du sémaphore :
I
compteur
I
liste des processus en attente
11
c
Pierre
David, [email protected]
Implémentation avec attente passive
Nouvelle définition de P et V (non atomique) :
P (S) {
S . cpteur - - ;
if ( S . cpteur < 0) {
ajouter_fifo ( S . liste , ps_courant ) ;
attendre () ;
}
}
V (S) {
S . cpteur ++ ;
if ( S . cpteur <= 0) {
ps = extraire_fifo ( S . liste ) ;
reveiller ( ps ) ;
}
}
I
I
cpteur > 0 ⇒ nombre de ressources disponibles
cpteur < 0 ⇒ nombre de processus en attente
12
c
Pierre
David, [email protected]
Implémentation avec mutex
La définition précédente ne garantit pas l’atomicité de P et V
I
I
I
il faut que P soit exécuté en section critique
⇒ mutex S.verrou_p
il faut que les modifications du compteur soient atomiques
⇒ mutex S.verrou_cpteur
pour réaliser l’attente, on utilise un mutex
⇒ mutex S.attente
⇒ état initial : verrouillé
13
c
Pierre
David, [email protected]
Implémentation avec mutex
P (S) {
lock ( S . verrou_p ) ;
lock ( S . verrou_cpteur ) ;
s . cpteur - - ;
if ( s . cpteur < 0) {
unlock ( S . verrou_cpteur ) ;
lock ( S . attente ) ;
} else {
unlock ( S . verrou_cpteur ) ;
}
unlock ( S . verrou_p ) ;
}
V (S) {
lock ( S . verrou_cpteur ) ;
s . cpteur ++ ;
if ( s . cpteur <= 0) {
unlock ( S . attente ) ;
}
unlock ( S . verrou_cpteur ) ;
}
14
c
Pierre
David, [email protected]
Plan
Principes
Implémentation
Sémaphores IPC System V
Sémaphores POSIX
Problèmes classiques de synchronisation
15
c
Pierre
David, [email protected]
IPC System V
IPC System V : objet « ensemble de sémaphores »
I
objet très sophistiqué
I
opérations atomiques sur un ensemble de sémaphores
Exemple : si je peux faire un P sur s[0] et sur s[1], je fais
également un V sur s[2]
I
⇒ interface de programmation complexe !
c
Pierre
David, [email protected]
16
IPC System V
Primitives d’accès :
I
semget : crée ou accède à un objet de type « ensemble de
sémaphores »
I
semctl : opérations (p. ex. initialisation ou suppression) sur
un ensemble de sémaphores
I
semop : opérations P et V sur sémaphores
c
Pierre
David, [email protected]
17
IPC System V
Opérations spécifiques des sémaphores :
I semctl :
I
I
I
GETVAL ou SETVAL d’un sémaphore de l’ensemble
GETALL ou SETALL : nécessite un tableau pour les valeurs
semop : utilise un tableau de structures sembuf
⇒ spécifie les opérations à réaliser sur l’ensemble
struct sembuf {
unsigned short sem_num ;
short
sem_op ;
short
sem_flg ;
} ;
18
/* numero de semaphore */
/* P : -1 , V :+1 */
/* 0 */
c
Pierre
David, [email protected]
IPC System V
int c reer_semaphore ( int val )
{
key_t k ;
int id ;
k = ftok ( " / etc / passwd " , ’S ’) ;
if ( k == -1)
erreur ( " ftok " ) ;
id = semget (k , 1 , IPC_CREAT | 0666) ;
if ( id == -1)
erreur ( " semget " ) ;
if ( semctl ( id , 0 , SETVAL , val ) == -1)
erreur ( " semctl ␣ setval " ) ;
}
return id ;
19
c
Pierre
David, [email protected]
IPC System V
int ac ced er_ sem aph ore ( void )
{
key_t k ;
int id ;
k = ftok ( " / etc / passwd " , ’S ’) ;
if ( k == -1)
erreur ( " ftok " ) ;
id = semget (k , 0 , 0) ;
if ( id == -1)
erreur ( " semget " ) ;
}
return id ;
20
c
Pierre
David, [email protected]
IPC System V
void s u p p r i m e r _ s em a p h o r e ( int id )
{
int r ;
}
r = semctl ( id , 0 , IPC_RMID , NULL ) ;
if ( r == -1)
erreur ( " semctl " ) ;
21
c
Pierre
David, [email protected]
IPC System V
void P ( int id )
{
struct sembuf s [1] = { {0 , -1 , 0} } ;
}
if ( semop ( id , s , 1) == -1)
erreur ( " semop " ) ;
void V ( int id )
{
struct sembuf s [1] = { {0 , 1 , 0} } ;
}
if ( semop ( id , s , 1) == -1)
erreur ( " semop " ) ;
22
c
Pierre
David, [email protected]
Plan
Principes
Implémentation
Sémaphores IPC System V
Sémaphores POSIX
Problèmes classiques de synchronisation
23
c
Pierre
David, [email protected]
Sémaphores POSIX
Sémaphores IPC System V : complexes pour les cas simples
POSIX a normalisé une API simple et homogène
plus proche de la définition de Dijkstra
24
c
Pierre
David, [email protected]
Sémaphores POSIX
Pour utiliser les sémaphores POSIX :
I
fichier d’inclusion : #include <semaphore.h>
I
édition de liens : cc -o ...-l pthread
Note : l’API n’est pas spécifique aux threads
⇒ peut être utilisée pour synchroniser des processus
Type :
I
sem_t : identificateur de sémaphore
25
c
Pierre
David, [email protected]
Sémaphores POSIX
Fonctions de l’API :
I
création et initialisation de sémaphore :
int sem_init (sem_t *sem, int prtg, unsigned int val)
I
I
I
prtg = 0 : accès limité aux threads du processus courant
prtg 6= 0 : accès à tous les processus qui partagent la
mémoire *sem
suppression de sémaphore :
int sem_destroy (sem_t *sem)
I
lecture du compteur :
int sem_getvalue (sem_t *sem, int *val)
26
c
Pierre
David, [email protected]
Sémaphores POSIX
Fonctions de l’API :
I
P:
int sem_wait (sem_t *sem)
I
P sans attente :
int sem_trywait (sem_t *sem)
I
I
P avec attente bornée :
int sem_timedwait (sem_t *sem, struct timespec *habs)
Avec struct timespec { time_t tv_sec; long tv_nsec; }
⇒ comme dans pthread_mutex_timedlock
V:
int sem_post (sem_t *sem)
27
c
Pierre
David, [email protected]
Plan
Principes
Implémentation
Sémaphores IPC System V
Sémaphores POSIX
Problèmes classiques de synchronisation
c
Pierre
David, [email protected]
28
Problèmes classiques de synchronisation
Problèmes classiques de concurrence
⇒ Illustration des mécanismes de synchronisation
1. producteur/consommateur sur tampon infini
2. producteur/consommateur sur tampon borné
3. lecteurs/écrivains avec priorité aux lecteurs
⇒ souvent rencontrés dans les cas réels
c
Pierre
David, [email protected]
29
Producteur/consommateur / tampon infini
Énoncé :
I plusieurs processus produisent des données
I plusieurs processus consomment des données
I hypothèse irréaliste (mais pédagogique) : tableau de taille
infinie
cases produites
et déja consommées
cases produites
et non consommées
0
6
1
2
3
4
5
7
8
9
cases non
encore produites
10 11 12 13 14 15 16 17
indice de la première
case à consommer
...
indice de la prochaine
case à produire
producteur
producteur
consommateur
consommateur
consommateur
30
c
Pierre
David, [email protected]
Producteur/consommateur / tampon infini
init () {
ip = ic = 0 ;
c re er_semaphore ( Sip , 1) ;
c re er_semaphore ( Sic , 1) ;
c re er_semaphore ( Secrites , 0) ;
}
produire ( val ) {
P ( Sip ) ;
tampon [ ip ] = val ; ip = ip + 1 ;
V ( Sip ) ;
V ( Secrites ) ;
}
consommer () {
P ( Secrites ) ;
P ( Sic ) ;
val = tampon [ ic ] ; ic = ic + 1 ;
V ( Sic ) ;
return val ;
}
c
Pierre
David, [email protected]
31
Producteur/consommateur / tampon infini
Trois sémaphores, pour :
I
I
I
protéger les accès concurrents à l’indice de la première
case à consommer
⇒ sémaphore binaire
protéger les accès concurrents à l’indice de la prochaine
case à produire
⇒ sémaphore binaire
représenter le nombre de cases produites (i.e. que l’on
peut consommer)
⇒ sémaphore général
c
Pierre
David, [email protected]
32
Producteur/consommateur / tampon borné
Énoncé : idem précédemment, mais tampon de taille bornée
cases produites
0
1
2
3
4
cases disponibles
5
6
7
8
9
cases produites
10 11 12 13 14 15 16 17 18 19
indice de la prochaine
case à produire
indice de la première
case à consommer
consommateur
producteur
consommateur
consommateur
producteur
33
c
Pierre
David, [email protected]
Producteur/consommateur / tampon infini
init () {
ip = ic = 0 ;
c re er_semaphore ( Sip , 1) ;
c re er_semaphore ( Sic , 1) ;
c re er_semaphore ( Secrites , 0) ;
c re er_semaphore ( Slibres , N ) ;
}
produire ( val ) {
P ( Slibres )
P ( Sip ) ;
tampon [ ip ] = val ; ip = ( ip + 1) % N ;
V ( Sip ) ;
V ( Secrites ) ;
}
consommer () {
P ( Secrites ) ;
P ( Sic ) ;
val = tampon [ ic ] ; ic = ( ic + 1) % N ;
V ( Sic ) ;
V ( Slibres )
return val ;
}
34
c
Pierre
David, [email protected]
Producteur/consommateur / tampon borné
Un sémaphore supplémentaire, pour :
I
représenter le nombre de cases libres (i.e. que l’on peut
produire)
⇒ sémaphore général
35
c
Pierre
David, [email protected]
Lecteurs/écrivains avec priorité aux lecteurs
Énoncé :
I
plusieurs processus peuvent accéder à une donnée
I
I
I
les accès en lecture peuvent être concurrents
un accès en écriture est exclusif de tout autre accès
(en lecture comme en écriture)
un écrivain ne peut accéder à la donnée que si plus aucun
lecteur n’y accède
⇒ priorité aux lecteurs
36
c
Pierre
David, [email protected]
Lecteurs/écrivains avec priorité aux lecteurs
Problème typique de l’accès aux données complexes.
Exemples :
I
struct timespec
⇒ si la donnée est modifiée entre la lecture de tv_sec et
tv_nsec, le résultat est inintelligible
I
accès à une ligne d’une table de SGBD
37
c
Pierre
David, [email protected]
Lecteurs/écrivains avec priorité aux lecteurs
init () {
c re er_semaphore ( Secrivain , 1) ;
c re er_semaphore ( Smutex , 1) ;
nlect = 0 ;
}
ecrivain () {
P ( Secrivain ) ;
/* ecriture */
V ( Secrivain ) ;
}
lecteur () {
P ( Smutex ) ;
nlect ++ ;
if ( nlect == 1) {
P ( Secrivain )
}
V ( Smutex ) ;
/* lecture */
P ( Smutex ) ;
nlect - - ;
if ( nlect == 0) {
V ( Secrivain )
}
V ( Smutex ) ;
}
38
c
Pierre
David, [email protected]
Lecteurs/écrivains avec priorité aux lecteurs
Deux sémaphores, pour :
I
I
protéger les accès concurrents à nlect
⇒ sémaphore binaire
protéger l’accès en écriture exclusivement de tout autre
accès
⇒ sémaphore binaire
39
c
Pierre
David, [email protected]