Synchronisation§

Systèmes d’exploitation

auteur:Pierre-Antoine Champin
adresse:Département Informatique - IUT - Lyon1
licence:Ce travail est sous license Creative Commons Attribution-ShareAlike 3.0 France
1

Ressource critique§

2

Problématique§

Lorsque deux tâches (processus ou threads) accèdent à une même zone mémoire, elles peuvent interagir de manière indésirable,

3

Concurence sans problème§

_images/resource_critique_1.png
4

Concurence problématique§

_images/resource_critique_2.png
5

Définitions§

6

Exclusion mutuelle§

7

Mise en œuvre abstraite§

int compte;                                   /* commun */
void entrer_SC_compte();
void sortir_SC_compte();

printf("ajout de 1000€\n");                  /* tâche 1 */
entrer_SC_compte();
compte += 1000;
sortir_SC_compte();
printf("ajout effectué\n");

printf("retrait de 500€\n");                 /* tâche 2 */
entrer_SC_compte();
compte -= 500;
sortir_SC_compte();
printf("retrait effectué\n");
8

Mutex: Critères d’évaluation§

9

Mauvaise solution : attente active§

On utilise un booléen partagé comme « verrou »

int verrou = 0;

void entrer_SC() { while (verrou) {}
                   verrou = 1; }

void sortir_SC() {  verrou = 0; }
10

Défauts de l’attente active§

11

Bonne solution§

12

Mise en œuvre concrète§

int compte;                                   /* commun */
sem_t mutex_compte;
status = sem_init(&mutex_compte, 1, 1);

printf("ajout de 1000€\n");                  /* tâche 1 */
status = sem_wait(&mutex_compte);
compte += 1000;
status = sem_post(&mutex_compte);
printf("ajout effectué\n");

printf("retrait de 500€\n");                 /* tâche 2 */
status = sem_wait(&mutex_compte);
compte -= 500;
status = sem_post(&mutex_compte);
printf("retrait effectué\n");
13

Remarques§

14
Remarques (2)§

Les sections critiques sont un mal nécessaire:

Conséquence :

15

Remarques (3)§

/* n'écrivez PAS --------------------------------- */
status = sem_wait(&mutex_compte);
printf("ajout de 1000€\n");
compte += 1000;
printf("ajout effectué\n");
status = sem_post(&mutex_compte);

/* mais écrivez ---------------------------------- */
printf("retrait de 500€\n");
status = sem_wait(&mutex_compte);
compte -= 500;
status = sem_post(&mutex_compte);
printf("retrait effectué\n");
16

Inter-blocage§

Un inter-blocage (deadlock) est une situation où plusieurs tâches sont bloquées car chacune attend un événement que doit produite une autre.

Un exemple classique se produit lorsque deux tâche attendent chacune un « jeton » détenu par l’autre :

/* tâche 1 */                    /* tâche 2 */
sem_wait(&mutex_A);              sem_wait(&mutex_B);
utilise(A);                      utilise(B);
sem_wait(&mutex_B);              sem_wait(&mutex_A);
utilise(A, B);                   utilise(A, B);
/* ... */                        /* ... */
17

Solution possible§

Une manière d’éviter les inter-blocages consiste à toujours réclamer les ressources dans le même ordre (quitte à libérer une ressource avant de la réclamer à nouveau).

/* tâche 1 */                    /* tâche 2 */
sem_wait(&mutex_A);              sem_wait(&mutex_B);
utilise(A);                      utilise(B);
sem_wait(&mutex_B);              sem_post(&mutex_B);
utilise(A, B);                   sem_wait(&mutex_A);
/* ... */                        sem_wait(&mutex_B);
                                 utilise(A, B);
                                 /* ... */
18

Problèmes typiques de synchronisation§

Pas une liste exhaustive, mais une source d’inspiration.

19

Producteurs/consommateurs§

20

Exemple§

_images/prod-cons_1.png
_images/prod-cons_2.png
_images/prod-cons_3.png
_images/prod-cons_4.png
_images/prod-cons_5.png
_images/prod-cons_6.png
_images/prod-cons_7.png
21

Problème§

22

Principe de solution§

23
Principe de solution (2)§
24

Algorithme§

elt_t tab[N]
sem_t libres, occupes, mutex;
sem_init(&libres,  1, N);
sem_init(&occupes, 1, 0);
sem_init(&mutex,   1, 1);

void producteur() {
  elt_t e = produire();
  sem_wait(&libres);
    sem_wait(&mutex);
      ajouter(e, tab);
    sem_post(&mutex);
  sem_post(&occupes);
}
void consommateur() {
  sem_wait(&occupes);
    sem_wait(&mutex);
      elt_t e = retirer(tab);
    sem_post(&mutex);
  sem_post(&libres);
  consommer(e);
}
25

Remarques§

26

Lecteurs/rédacteurs§

27

Problème§

28

Principe de solution§

29

Algorithme§

int c = 0;                 //
sem_t mutex_m, mutex_c;
sem_init(&mutex_m,  1, 1);
sem_init(&mutex_c,  1, 1);



void redacteur() {
  sem_wait(&mutex_m);
    ecrire(e, tab);
  sem_post(&mutex_m);
}
void lecteur() {              //
  sem_wait(&mutex_c);
    if (c == 0)
      sem_wait(&mutex_m);
    c += 1;
  sem_post(&mutex_c);

    lire();

  sem_wait(&mutex_c);
    if (c == 1)
      sem_post(&mutex_m);
   c -= 1;
  sem_post(&mutex_c);
}
30

Remarques§

31

Philosophes§

_images/philosophers.png

Illustration : © Benjamin Esham

Problème proposé par Dijkstra.

Cinq philosophes passent leur vie à penser et manger autour d’une table.

Pour manger, ils ont besoin de deux fourchettes, mais il n’y a que cinq fourchettes.

32

Problème§

33

Principe de solution§

34

Algorithme§

sem_t mutex;     // init à 1
sem_t reveils[N];// init à 0
int etats[n];    // init à PENSE

void CommenceManger(int id) {
  sem_wait(mutex);
  etats[id] = FAIM;
  bool ok = etat[id-1] != MANGE
         && etat[id+1] != MANGE;
  if (ok) {
    etat[id] = MANGE;
    sem_post(mutex);
  } else {
    sem_post(mutex);
    sem_wait(reveils[id]);
  }
}
void FinitManger(int id) {
  sem_wait(mutex);
  etats[id] = PENSE;
  /* voisin gauche */
  if (etat[id-1] == FAIM
  &&  etat[id-2] != MANGE) {
    etat[id-1] = MANGE;
    sem_post(reveils[id-1]);
  }
  /* voisin droit */
  if (etat[id+1] == FAIM
  &&  etat[id+2] != MANGE) {
    etat[id+1] = MANGE;
    sem_post(reveils[id+1]);
  }
  sem_post(mutex);
}
35

Remarques§

36

Mutex internes au système§

37

Motivation§

38

Problème§

Les procédures sem_wait et sem_post sont des sections critiques pour le sémaphore lui même…

proc sem_wait(sem):                                         .
  si sem.c = 0 alors
    bloquer le processus courant
  finsi
  sem.csem.c - 1

proc sem_post(sem)
  sem.csem.c + 1
  si des processus sont bloqués
    en attente du sémaphore
  alors
    débloquer un des processus
  finsi
39

Sections critiques dans un sémaphore§

proc sem_wait(sem):
  entrer_SC()
  si sem.c = 0 alors
    sortir_SC()
    bloquer le processus courant
  sinon
    sem.csem.c - 1
    sortir_SC()
  finsi
proc sem_post(sem):
  entrer_SC()
  sem.csem.c + 1
  si des processus sont bloqués
    en attente du sémaphore
  alors
    débloquer un des processus
    sem.c ← sem.c - 1
 finsi
 sortir_SC()
40

Exclusion mutuelle de bas niveau§

41

Autres solutions§

42

Conclusion§

43

À retenir§

44