Linux Principes et Programmation 4. Synchronisation entre processus Interblocage CNAM NSY103 2009/2010 Yann GAUTHERON Extraits : http://fr.wikipedia.org/, LINUX Programmation système et réseau (Joëlle DELACROIX) http://www.cnam.fr/
Les schémas de synchronisation L'exclusion mutuelle Le schéma de l'allocation de ressources Le schéma lecteur-rédacteur Le schéma producteurs-consommateurs Sémaphores Sous Linux Mutex Sous linux Interblocage Différentes politiques
Notions de base Système multiprocessus communicants = accès concurrent aux ressources (mémoire, outils de communication...) Une ressource = toute entité dont a besoin un processus pour l'exécuter Matérielle (processeur, périphérique...) Logicielle (fichier) État de ressource Libre ou occupée Nombre de points d'accès Ressource critique = ne peut être utilisée que par un seul processus à la fois Utilisation de ressource Allocation Utilisation Restitution
L'exclusion mutuelle Exemple simple : 1. Réservation() { 2. if (nbplace > 0) { 3. // Réserver une place 4. nbplace = nbplace 1; 5. } 6. } Le programme Réservation() peut s'exécuter de manière concurrente Sans précaution, la variable nbplace peut atteindre une valeur inférieure à 0 P1 exécute le test de la ligne 2, trouve 1>0, puis est préempté par P2. P2 démarre, exécute la ligne 2, trouve également 1>0, décrémente nbplace puis se termine. P1 reprend ligne 3, décrémente nbplace puis se termine. nbplace=-1!
L'exclusion mutuelle
L'exclusion mutuelle
L'exclusion mutuelle Ressource critique La variable nbplace doit être accédée par un seul processus à la fois nbplace est donc une ressource critique Ainsi P2 trouverait la valeur à 0 et ne décrémenterait pas la variable! Section critique Propriété d'exclusion mutuelle Garantie que la ressource n'est utilisée qu'une 1 fois simultanément Propriété d'attente bornée Propriété du bon déroulement
L'exclusion mutuelle
L'exclusion mutuelle Réalisation «matérielle» d'une section critique Par masquage/démasquage 1. Réservation() { 2. disable_interrupt; // prélude de section critique 3. if (nbplace > 0) { 4. // Réserver une place 5. nbplace = nbplace 1; 6. } 7. enable_interrupt; // postlude de section critique 8. } Cette solution empêche l'exécution de tous les processus (même ceux qui ne veulent pas nbplace) Cette méthode de masquage et démasquage est réservée à l'os pour les parties sensibles (et non au mode utilisateur)
L'exclusion mutuelle Réalisation «matérielle» d'une section critique Test and set : instruction matérielle atomique 1. #define VRAI 1 2. #define FAUX 0 3. while TS(cadenas); // prélude de section critique 4. // section critique 5. cadenas = FAUX // postlude de section critique 6. int function TS(int *verrou) { 7. disable_interrupt; 8. int test = *verrou; 9. *verrou = VRAI; 10. return test; 11. enable_interrupt; 12.} Cette solution ne garantit pas l'équité d'accès à la section critique
L'exclusion mutuelle Réalisation «matérielle» d'une section critique Swap : seconde instruction atomique qui inverse les valeurs de 2 variables 1. #define VRAI 1 2. #define FAUX 0 3. cle = VRAI; // prélude de section critique 4. while (cle == VRAI) 5. swap(verrou,cle); 4. // section critique 5. verrou = FAUX // postlude de section critique
L'exclusion mutuelle Réalisation entièrement logicielle d'une section critique
L'exclusion mutuelle Réalisation entièrement logicielle d'une section critique Algorithme de Peterson Exclusion entre deux processus i et j, structure de i : 1. while { // prélude 2. drapeau[i]=vrai; tour=j; 3. while (drapeau[j] && tour==i) 4. // section critique 5. drapeau[i]=faux // postlude 6. } Exclusion mutuelle : un seul entrera en SC Attente bornée : impossible d'entrer deux fois de suite en SC Bon déroulement : même si arrivée simultanée, l'un peut toujours passer en SC
L'exclusion mutuelle L'outil Sémaphore
L'exclusion mutuelle L'outil Sémaphore Outil système File d'attente L + compteur K (niveau) «Distributeur de jetons» contenant val jetons initialement Obtention d'un jeton donne le droit d'accès à la SC, sinon mise en attente 3 opérations atomiques : P(sem)uis-je?, V(sem)as-y!, Init(sem,val) Initialisation du Sémaphore : 1. function Init(semaphore sem, int val) { 2. disable_interrupt; 3. sem.k = val; 4. sem.l = NULL; 5. enable_interrupt; 6. }
L'exclusion mutuelle L'outil Sémaphore Attribution de jeton (Puis-je?) 1. function P(semaphore sem) { 2. disable_interrupt; 3. sem.k = sem.k - 1; 4. if (sem.k < 0) { 5. L.suivant = processus_courant; 6. processus_courant.state= bloque; 7. reordonnancement = vrai; 8. } 9. enable_interrupt; 10. }
L'exclusion mutuelle L'outil Sémaphore Rendre un jeton, et peut-être réveiller un autre processus (Vas-y!) 1. function V(semaphore sem) { 2. disable_interrupt; 3. sem.k = sem.k + 1; 4. if (sem.k <= 0) { // 1 process dans la file 5. processus_reveille= L.tete; 6. processus_reveille.state = prêt; 7. reordonnancement = vrai; 8. } 9. enable_interrupt; 10. }
L'exclusion mutuelle L'outil Sémaphore Signification du compteur K K >= 0 : K = nombre d'opérations P() encore passantes K < 0 : K = nombre de processus dans la file d'attente L Exemple d'utilisation : 1. Init(Mutex, 1); 2. Reservation { 3. P(Mutex); // prélude 4. if (nbplace > 0) // section critique 5. nbplace = nbplace - 1; 6. V(Mutex); // postlude 7. }
L'exclusion mutuelle L'outil Sémaphore
L'exclusion mutuelle L'outil Sémaphore
Schéma d'allocation de ressources Exemple : Sémaphores utilisés pour représenter et compter des exemplaires de ressources Schéma lecteurs-rédacteurs Exemple : Fichiers accédés simultanément en lecture et en écriture. Les lecteurs sont les processus qui lisent, les rédacteurs sont ceux qui y écrivent. Deux objectifs : protéger les zones d'accès concurrents, toujours conserver un état stable (ne pas lire ce qui est en cours d'écriture), il faut donc : Soit une seule écriture en cours ; Soit une ou plusieurs lectures en cours. Il peut surgir des problèmes de coalition Sous Linux, les outils sont : int flock(int fd, int operation); int lockf(int fd, int operation, off_t taille);
Schéma producteurs-consommateurs Principe : Un processus crée des messages, et les dépose dans une file composée de 0 à n cases, puis revient à la case 0. Un processus lit les n cases puis revient à la case 0. Règles : Ne pas produire si tampon plein Ne pas consommer si tampon vide Ne pas produire et consommer dans une même case en même temps Solution : Symboliser deux ressources «case vide» et «case pleine» : Producteur : consomme case vide, produit case pleine Consommateur : consomme case pleine, produit case vide Traiter le problème comme une allocation de ressources Sous Linux : Illustré par les MSQ, et les Tubes
Sémaphores sous Linux IPC : Un ensemble de sémaphore est identifié par une clé P, V et ATT (Attente qu'une valeur soit nulle) s'effectuent atomiquement sur un tableau de sémaphores. L'ensemble des opérations est réalisé avant que le processus puisse poursuivre son exécution. 1. struct semaphore { 2. atomic_t count; // compteur du sémaphore 3. int sleepers; // nombre de processus endormis 4. wait_queue_head_t wait; // file de ces processus 5. }
Sémaphores sous Linux Création et recherche int semget(key_t cle, int nsems, int semflg) Semflg : constantes IPC_EXCL, IPC_CREAT, 0 Opérations : int semop(int semid, struct sembuf *sops, unsigned nsops) 1. struct sembuf { 2. unsigned short sem_num; // numero 3. short sem_op; // opération à réaliser 4. short sem_flg; // options 5. }
Sémaphores sous Linux Contrôle : int semctl(int semid, int semnum, int cmd, union semun arg) semctl(semid, 0, SETVAL, 3); initialisation à la valeur 3 du sémaphore 0 dans l'ensemble désigné par l'identifiant semid. Destruction : semctl(intsemid, 0, IPC_RMID, 0);
#include <stdio.h> #include <pthread.h> #include <sys/ipc.h> #include <sys/sem.h> int i, nb_place; int semid; struct sembuf operation; void reservation(){ operation.sem_num = 0; operation.sem_op = -1; operation.sem_flg = 0; semop(semid, &operation, 1); /* opération P */ nb_place= nb_place-1; /* section critique */ operation.sem_num = 0; operation.sem_op = 1; operation.sem_flg = 0; semop(semid, &operation, 1); /* opération V */ } main(){ pthread_t num_thread[3]; semid = semget(12, 1, IPC_CREAT IPC_EXCL 0600);// création semctl(semid, 0, SETVAL, 1);// Init à valeur 1 for(i=0; i<3; i++) pthread_create(&num_thread[i],null,(void *(*)())reservation,null); pthread_join(num_thread, NULL); semctl(semid, 0, IPC_RMID, 0); }
Mutex Outil de synchronisation de type pthread_mutex_t servant de verrou Protection de zones de codes ou de données particulières Deux états : disponible ou verrouillé Accessible uniquement par un thread à la fois Initialisation : int pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; Verrouillage : int pthread_mutex_lock(pthread_mutex_t *mut); int pthread_mutex_trylock(pthread_mutex_t *mut); Libération : int pthread_mutex_unlock(pthread_mutex_t *mut); Destruction : int pthread_mutex_destroy(pthread_mutex_t *mut);
Interblocage
Interblocage Ensemble de n processus attendant chacun une ressource déjà possédée par un autre processus de l'ensemble : Aucun processus ne peut poursuivre son exécution Attente infinie
Coalition n processus monopolisant les ressources au détriment de p autres : Famine Attente finie mais indéfinie
Interblocage Exemple : Client : Attente de l'ouverture en lecture du tube 1 1. tub1 = open ("tube1", O_WRONLY); // écriture 2. tub2 = open ("tube2", O_RDONLY); // lecture Serveur : Attente de l'ouverture en lecture du tube 2 1. tub2 = open ("tube2", O_WRONLY); // écriture 2. tub1 = open ("tube1", O_RDONLY); // lecture
Interblocage 4 conditions simultanées nécessaires : Exclusion mutuelle Au moins 1 ressource en situation non partageable Occupation & Attente Un processus au moins attend une ressource bloquée ailleurs Pas de réquisition Impossibilité de préemption Attente circulaire Cycle d'attente, ces processus sont en interblocage
Attente circulaire
Politiques de traitement de l'interblocage Guérison Le permettre mais le corriger Prévention ou évitement Ne pas le permettre Autruche Ignorer le problème
Politique de guérison Le système maintient un graphe représentant l'allocation des ressources et les attentes des processus Régulièrement, le système parcourt le graphe à la recherche de cycles Si un cycle est découvert, celui-ci est cassé en avortant les processus en interblocage appartenant au cycle Cette politique est coûteuse
Politique de prévention Rendre impossible au moins l'une des conditions nécessaires : Exclusion mutuelle : difficile Occupation et attente : demander les ressources en une seule fois Pas de réquisition : difficile Attente circulaire : ordre total sur l'ordre de demandes de ressources
Politique de prévention Occupation et attente : demander les ressources en une seule fois Mauvaise utilisation des ressources
Politique de prévention Attente circulaire : ordre total sur l'ordre de demandes de ressources Unité de bandes avant le disque et avant l'imprimante
Politique d'évitement Examen dynamique de l'état des ressources pour éviter l'attente circulaire A chaque demande d'allocation, le système détermine si accepter conservera le système sain Allocation peut être refusée Vision pessimiste
Politique d'évitement Cas 1 12 exemplaires de ressources au total Le nombre de ressources disponibles est égal à 3. La séquence d exécution < P2, P1, P3 > est saine: satisfaction de P2, ressources disponibles = 1; restitution des ressources par P2, ressources disponibles = 5; satisfaction de P1, ressources disponibles = 0; restitution des ressources par P1, ressources disponibles = 10; satisfaction de P3, ressources disponibles = 3; restitution des ressources par P3, ressources disponibles = 12.
Politique d'évitement Cas 2 12 exemplaires de ressources au total Le nombre de ressources disponibles est égal à 2 L'état devient malsain et aucune séquence d'exécution incluant les trois processus ne peut être construite. Ici, seul P2 peut être satisfait : satisfaction de P2, ressources disponibles = 0; restitution des ressources par P2, ressources disponibles = 4. Maintenant, ni P3, ni P1 ne peuvent être satisfaits.
Politique de l'autruche Prétendre que les interblocages ne se produisent jamais et ne rien prévoir Un interblocage peut se produire et n'est pas détecté Détérioration des performances jusqu'à arrêt complet du système Redémarrage manuel du système
Politique de l'autruche Justification de ce choix Fréquence de l'interblocage
Le noyau Linux Le noyau dispose de 3 méthodes pour s'assurer de la cohérence de ses structures : Le noyau est non préemptible Le noyau peut masquer les interruptions Le noyau dispose de sémaphores Interblocage : Le noyau impose un ordre total sur les demandes d'allocations de ressources : Requêtes émises selon l'ordre croissant des adresses en mémoire centrale