Systèmes d'exploitation Pierre Antoine Champin IUT A de Lyon Séance 4
Plan de la séance 1. Concurrence 2. Problématique liée à la concurrence section critique mécanisme d'exclusion mutuelle 3. Solutions internes au SE logicielles / matérielle 4. Solution offerte par le SE sémaphore
1. Concurrence L'accès «simultané» a une ressource commune peut se produire : au sein du système d'exploitation (structures internes au système) entre deux threads entre deux processus communicants Il peut poser des problèmes de cohérence
Accès concurrent sans problème tâche 1 (compte += 1000) tâche 2 (compte -= 500) ressources compte r1 r2 charger (r1, compte) charger (r2, 1000) ajouter (r1, r2) écrire (r1, compte) sauvegarde contexte 2000 2000 0 2000 2000 1000 2000 3000 1000 3000 3000 1000 3000 1000 restauration contexte 0 0 charger (r1, compte) 3000 3000 0 charger (r2, 500) soustraire (r1, r2) écrire (r1, compte) 3000 3000 500 3000 2500 500 2500 2500 500
Accès concurrent problématique tâche 1 (compte += 1000) tâche 2 (compte -= 500) ressources compte r1 r2 charger (r1, compte) 2000 2000 0 charger (r2, 1000) 2000 2000 1000 ajouter (r1, r2) 2000 3000 1000 commutation de contexte 0 0 charger (r1, compte) 2000 2000 0 charger (r2, 500) 2000 2000 500 soustraire (r1, r2) 2000 1500 500 écrire (r1, compte) 1500 1500 500 commutation de contexte 3000 1000 écrire (r1, compte) 3000 3000 1000
2. Définitions On appelle section critique une portion de code qui, si elle est interrompue, risque de rendre incohérente une structure partagée On appelle mécanisme d'exclusion mutuelle tout dispositif visant à «protéger» une section critique des interruptions (en anglais : mutual exclusion, ou mutex)
Section critique : exemple entier compte proc entrer_sc_compte () proc sortir_sc_compte () afficher («Ajout de 1000») entrer_sc_compte () compte += 1000 sortir_sc_compte () afficher («Ajout effectué») afficher («Retrait de 500») entrer_sc_compte () compte -= 500 sortir_sc_compte () afficher («Retrait effectué»)
Exclusion mutuelle : critères Exclusion : deux tâches ne doivent pas se trouver en même temps en SC Progression : une tâche doit pouvoir entrer en SC si aucune autre ne s'y trouve Équité : une tâche ne devrait pas attendre indéfiniment pour entrer SC L'exclusion mutuelle doit fonctionner dans un contexte multi-processeurs
3. Solutions internes au SE Ces solutions sont mis en œuvre «en dessous» des processus ; ils ne peuvent donc pas utiliser la notion de blocage elles ont (en général) recours à l'attente active
Types de solution Logicielles attente active Matérielles interruptions instruction dédiée
Solution logicielle... booléen verrou FAUX proc entrer_sc tant que verrou faire rien verrou VRAI proc sortir_sc verrou FAUX
Solution logicielle... fausse! booléen verrou FAUX proc entrer_sc tant que verrou faire rien verrou VRAI proc sortir_sc verrou FAUX # pas d'exclusion Si deux tâches cherchent à entrer en même temps en section critiques, elles sortiront toutes deux de la boucle (car le verrou n'est pas encore à VRAI), puis le positionneront toutes les deux à VRAI. L'exclusion n'est pas vérifiée.
Autres solutions fausses # deux tâches, 0 et 1 booléen verrou[2] [FAUX,FAUX] proc entrer_sc verrou[id] VRAI tant que verrou[1-id] faire rien proc sortir_sc verrou[id] FAUX # deux tâches, 0 et 1 booléen tour 0 proc entrer_sc tant que tour!= id faire rien proc sortir_sc tour 1 tour # pas de progression # pas de progression - deadlock
Algorithme de Peterson # deux tâches, numérotés 0 et 1 booléen verrou[2] [ FAUX, FAUX ] entier tour 0 # ou 1 proc entrer_sc verrou[id] VRAI tour 1-id tant que verrou[1-id] et tour id faire rien proc sortir_sc verrou[id] FAUX
Algorithme de la boulangerie (1) Chaque tâche possède un identifiant numérique Chaque tâche souhaitant entrer en SC prend un numéro (ticket) La tâche ayant le numéro le plus petit entre en SC En cas d'égalité des numéros, la tâche ayant le plus petit identifiant entre en SC
Algorithme de la boulangerie (2) booléen préparation[n] [ FAUX ] x n entier ticket[n] [ 0 ] x n proc entrer_sc préparation[id] VRAI ticket[id] max(ticket[i] pour i de 0 à n-1) + 1 préparation[id] FAUX pour i de 0 à n-1 faire tant que préparation[i] faire rien tant que ticket[i] 0 et (ticket[i],i) < (ticket[id],id) faire rien proc sortir_sc ticket[id] 0
Types de solution Logicielles attente active Matérielles interruptions instruction dédiée
Masquer les interruptions Assure l'exclusion, mais dégrade les performances pendant toute la SC (on perd le bénéfice des interruptions) Assure la progression mais pas l'équité Ne fonctionne que dans un contexte monoprocesseur
Instruction atomique TestAndSet Instruction spécialisée, teste une variable, si la variable vaut 0, la mettre à 1 et retourner 0 sinon, retourner 1 Algorithme (attente active) : proc entrer_sc tant que TestAndSet (verrou) faire rien Répond à toutes les exigences de l'exclusion mutuelle
4. Solutions internes au SE Le SE doit fournir aux programmeurs d'applications des mécanismes standard d'exclusion mutuelle ces mécanismes doivent bloquer les processus jusqu'à obtention de la ressource
Sémaphore - Principe Structure proposée par Dijkstra possède un compteur opération P / wait bloque jusqu'à ce que le compteur soit supérieur à 0 décrémente le compteur opération V / signal incrémente le compteur ce qui réveille les éventuels processus en attente les opérations wait et signal sont atomiques
Sémaphore : exemple entier compte sémaphore mutex nouveau sémaphore (1) afficher («Ajout de 1000») mutex.wait () compte += 1000 mutex.signal () afficher («Opération effectuée») afficher («Retrait de 500») mutex.wait () compte -= 500 mutex.signal () afficher («Opération effectuée»)
Sémaphore Implémentation (1) classe Semaphore attributs privés : entier c # compteur mutex m # «bas niveau», i.e. TestAndSet ou Peterson file f # parfois une autre structure méthodes : proc init (v) c v.../...
Sémaphore Implémentation (2) proc wait m.entrer_sc () si c = 0 alors masquer les interruptions f.ajouter (ce_processus) ce_processus.état BLOQUÉ m.sortir_sc () dé-masquer les interruptions rendre la main sinon c c 1 m.sortir_sc ().../...
Sémaphore Implémentation (3) proc signal m.entrer_sc () c c + 1 si f est non vide alors p = f.suivant () p.état ÉLIGIBLE c c - 1 # pour le processus qui vient d'être débloqué m.sortir_sc ()