Processus légers LIF12-Systèmes d Exploitation Yves Caniou yves.caniou@univ-lyon1.fr CM + TD + TP Jacques Delmas jacques.delmas@univ-lyon1.fr TD Fabien Rico fabien.rico@univ-lyon1.fr TP Joseph Garnier TP Romain Calliere TP Univ. Claude Bernard Lyon 1 séance 3 Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 1 / 40
1 Introduction Théorie Création et destruction des threads 2 Synchronisation Section critique Variables de condition Moniteur Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 2 / 40
Introduction Pourquoi? Processus Permet de partager un même matériel entre plusieurs utilisateurs. Isole les programmes Communications simplifiées mais encadrées. Sécurité. Stabilité. Notion des années 70 (plusieurs utilisateurs sur un gros serveur). Pas toujours adaptés (1 utilisateur voulant faire du calcul //). Parfois difficile à utiliser (exécuter une action précise parrallèlement au programme principal). Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 3 / 40
Introduction Exemple (Le mini chat) Le même processus doit pouvoir enregistrer les évènements de l utilisateur et attendre un message du réseau. 1 re idée de solution, il faut : Un processus qui lit sur le réseau (en attente). Un processus qui fait le reste (affichage, saisie du clavier...) Mais comment les faire communiquer sans se retrouver avec le même problème. 2 e idée : Un fil de traitement ou thread qui lit et écrit le résultat dans une variable. Un fil de traitement qui fait le reste (notamment l affichage de la variable). Une structure de données accessible par les deux pour communiquer (la fameuse variable). C est un problème de producteur/consommateur simplifié. La solution présentée a seulement pour but de présenter le cours. Ce n est pas la meilleure solution à ce problème. Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 4 / 40
Léger et lourd? Processus Contient tout ce qui est nécessaire à l exécution (registre du processeur, ressources, mémoire...). Commutation de contexte lente. Communication par des appels système. Programmation simple. Thread Un processus contient plusieurs threads Ces threads partagent les mêmes ressources, la même mémoire Commutation de contexte plus rapide Communications = partage de variables. Simple? Attention aux variables partagées
Introduction Théorie 1 Introduction Théorie Création et destruction des threads 2 Synchronisation Section critique Variables de condition Moniteur Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 6 / 40
Introduction Théorie Thread/processus Les processus sont des rassemblements de ressources pour un programme Les threads sont des chemins d exécution dans les processus Mais tout n est pas si simple Le processus doit-il réordonnancer ses threads? Le noyau a-t il connaissance des threads dans les processus? Les états prêt, en exécution, bloqué sont-il des états de threads? Que se passe-t il quand un thread fait un fork? P1 P2 Utilisateur Noyau CPU1 CPU2 CPU3 Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 7 / 40
Introduction Théorie Modèles - I Définition (thread utilisateur - green thread) Le noyau n a pas connaissance des threads. Portable. Gestion sans appel système. Ce n est qu une simulation. Définition (Thread lié - thread noyau) Un thread système pour chaque thread. Modèle simple. Gestion par appel système. Modèle sous linux 2.5-2.6. Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 8 / 40
Introduction Théorie Modèles - II Définition (Threads multiplexés) Entre les deux précédants. Plusieurs threads systèmes affectés à un processus qui a lui même plusieurs threads. Plus souple. Plus complexe. Modèle Windows xp et +. Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 9 / 40
Introduction Théorie Différences Thead/Processus Dans les 2 cas suivants, pour programmer le serveur, utiliseriez-vous des threads ou des processus et pourquoi? Serveur de connexion à distance (type terminal serveur ou ssh) Serveur de fichiers (type NFS ou SMB) Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 10 / 40
Introduction Création et destruction des threads 1 Introduction Théorie Création et destruction des threads 2 Synchronisation Section critique Variables de condition Moniteur Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 11 / 40
Introduction Création et destruction des threads Appels système Création du thread int pthread create( pthread t thread,// résultat : le thread créé pthread attr t attr, // Les attributs de création ou NULL void ( start routine )( void ),//La fonction que //le thread exécute void arg ); // L argument de la fonction Fin du thread void pthread exit(void retval ); int pthread cancel( pthread t thread ); Attente du résultat d un thread : int pthread join( pthread t th,// Le thread à attendre void thread return ); // résultat : sa valeur de retour Dans tous les cas la valeur de retour est 0 en cas de succès et un code d erreur non nul en cas d échec. Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 12 / 40
1 Introduction Théorie Création et destruction des threads 2 Synchronisation Section critique Variables de condition Moniteur Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 13 / 40
Retour sur l exemple Dans l exemple il y a un thread qui produit une information et un thread qui la lit. C est un problème de producteur/consommateur Très simplifié Un seul producteur Un seul consommateur Une file d attente avec une seule case On peut cependant voir les problèmes posés Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 14 / 40
Section critique 1 Introduction Théorie Création et destruction des threads 2 Synchronisation Section critique Variables de condition Moniteur Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 15 / 40
Section critique Section critique Exemple Le producteur (mini-chat) Dans l exemple du mini-chat, l échange des informations entre les deux threads utilise des variables globales partagées : // Pour signaler la présence d un nouveau message int nouveau message = FALSE; // Pour stocker le message en attente d être lu char message[taille BUFF ]; Afin de transmettre un nouveau message le producteur a deux actions à faire. nouveau message = TRUE; strncpy (message, mess, TAILLE BUFF); Que se passe-t il si le thread perd la main entre les deux? Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 16 / 40
Définition Définition (Section critique) On appelle section critique une ou plusieurs sections de code où il ne doit y avoir qu un thread à la fois. Pour ne pas accéder en même temps en écriture à des données partagées. Pour ne pas modifier une donnée qu on est en train de lire. À cause du multithreading il faut protéger l accès à ces sections critiques. En utilisant une variable partagée? while ( passage autorise) attend (); passage autorise = FALSE; Le problème est que le test de la variable et sa mise à jour forment une section critique. Il faut tester et modifier la variable en même temps.
Section critique Mutex Pour mettre en place les sections critiques on peut utiliser le système. Il propose des variables partagées qui peuvent être testées et modifiées de manière unitaire : les verrous ou mutex. Définition (mutex) Un mutex est une primitive de synchronisation, elle permet : de vérifier qu on est autorisé à passer d interdire le passage aux autres. Les deux actions sont faites en une seule instruction atomique (i.e. une instruction qui ne peut pas être interrompue). Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 18 / 40
Section critique Mutex statiques ou dynamique Pour la déclaration et l initialisation, comme pour les tableaux on peut utiliser 2 méthodes. mutex statique pthread mutex t mutex = PTHREAD MUTEX INITIALIZER; Permet de définir rapidement un mutex avec un comportement standard. mutex dynamique int pthread mutex init( pthread mutex t mutex, // le mutex à initialiser const pthread mutex attr t mutexattr ); // les attributs spéciaux ou NULL int pthread mutex destroy(pthread mutex t mutex); // destruction du mutex Permet d initialiser le mutex à volonté et de lui attribuer un comportement particulier. Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 19 / 40
Section critique Utilisation du mutex Une fois le vérou créé il faut des fonctions pour l acquérir et le libérer : // Pour acquérir le verrou et bloquer // s il appartient à un autre int pthread mutex lock(pthread mutex t mutex); // Pour essayer d acquérir sans bloquer int pthread mutex trylock(pthread mutex t mutex); // Pour relâcher le verrou int pthread mutex unlock(pthread mutex t mutex); Ces fonctions renvoient 0 en cas de succès et un code d erreur en cas de problème. pthread mutex init renvoie toujours 0. Si plusieurs threads sont en attente qui est réveillé? Comment tenir compte de l importance des threads? Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 20 / 40
Section critique lock ou trylock? pthread mutex lock bloque le thread pendant que la section critique est occupée. Pendant que le thread est bloqué il n utilise pas inutilement le processeur. Il sera réveillé lors de la libération du mutex. Cela permet de faire une attente passive pthread mutex trylock ne bloque pas le thread. Il est donc possible de faire autre chose puis de réessayer la section critique. Cela permet de faire une attente active. Un thread en section critique bloque les autres. Il faut minimiser la durée du séjour. Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 21 / 40
Variables de condition 1 Introduction Théorie Création et destruction des threads 2 Synchronisation Section critique Variables de condition Moniteur Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 22 / 40
Variables de condition Exemple Mini-chat Dans l exemple, tous les échanges se font grâce à une seule variable. Que se passe-t il si le consommateur est lent et que la variable est occupée? Dans l idéal, Si la variable est libre, le producteur l utilise Sinon? Une attente active utilise le processeur pour rien Une attente passive, comment? Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 23 / 40
Variables de condition Besoins Attente passive du producteur Si il n y a plus de place, attendre jusqu à ce que le consommateur dise qu il y a de la place. Pour une attente passive, il faut : Un moyen d attendre jusqu à un évènement. Peut-on utiliser les primitives d attente et les signaux (pause() et pthread kill )? Non car si le thread n attend pas il ne faut rien signaler. Il faut que le test et l attente soient unitaires. Il faut que le thread libère le mutex pendant l attente. Il faut que le thread ré-obtienne le mutex dès son déblocage (de façon unitaire). Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 24 / 40
Variables de condition appels système Définition (Variable de condition) La variable de condition est une variable partagée qui permet l attente dans une section critique. Initialisation statique d une variable; pthread cond t cond = PTHREAD COND INITIALIZER; Attente int pthread cond wait( pthread cond t cond,//la condition sur laquelle on attend pthread mutex t mutex); //Le mutex qu on libère int pthread cond timedwait (... //Même chose avec un délai maximum Réveiller un ou des thread(s) en attente int //Reveil d un thread pthread cond signal( pthread cond t cond ); int //Reveil de tous les threads un par un pthread cond broadcast( pthread cond t cond ); Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 25 / 40
Variables de condition Autre primitive Le sémaphore : Compteur initialisé avec une valeur positive. Il est décrémenté pour tester le passage. Il devient bloquant quand sa valeur est nulle. Plus ancien et plus général que le mutex. Le moniteur : Généralisation du concept de primitives de synchronisations. Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 26 / 40
Moniteur 1 Introduction Théorie Création et destruction des threads 2 Synchronisation Section critique Variables de condition Moniteur Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 27 / 40
Moniteur Comment aider le programmeur à utiliser les primitives de synchronisation? Encapsuler l utilisation des mutex et des conditions... Arbitrer l accès aux variables partagées Automatiser l acquisition et la libération des mutex. Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 28 / 40
Moniteur Moniteur de Hoare C est un concept pour aider le programmeur Notions de programmation orientée objet (cf. LIF5) : définir des accesseurs (accès en lecture), et des mutateurs (accès en écriture), une seule fonction peut être exécutée à la fois, les threads sont en exclusion mutuelle pour manipuler la structure partagée représentée par le moniteur. Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 29 / 40
Moniteur Comment faire? Certains langages permettent de définir des moniteurs (ex : en JAVA les méthodes synchronized) On peut simuler cela avec d autres langages à partir des mutex L objet moniteur contient un mutex Toutes les méthodes commencent par l acquisition du mutex et finissent par sa libération Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 30 / 40
Moniteur Le compteur Exemple (Compteur) Le compteur est une variable : partagée; accessible en lecture et écriture;... mais pas en même temps. Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 31 / 40
Moniteur Moniteur : exemple du compteur typedef struct { pthread mutex t mutex; int valeur ; } COMPTEUR; // initialisation et destruction // manipulations du compteur int compteur lire (COMPTEUR compteur ); void compteur ajouter(compteur compteur, int valeur ); Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 32 / 40
Moniteur Où est le problème? void compteur ajouter(compteur compteur, int valeur ) { compteur >valeur= compteur >valeur + valeur ; } int compteur lire (COMPTEUR compteur) { return compteur >valeur ; } Que peut-il se passer si on programme les fonctions de cette manière? Proposer un scénario avec 2 threads qui démontre le problème. Ce scénario est-il probable? Est-ce bon signe? Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 33 / 40
Moniteur Moniteur : exemple du compteur Le principe du moniteur est que le corps de chaque fonction est une section critique. Dans certains langages (JAVA) il suffit de spécifier cela à la déclaration de la fonction Exemple (En java) public class compteur { private int valeur = 0; public synchronized void ajouter ( int v) { valeur+=v; }... En C, cela n est pas possible directement : Il faut utiliser un verrou, le bloquer au début, et le relacher à la fin... Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 34 / 40
Moniteur Moniteur : exemple du compteur int compteur lire (COMPTEUR compteur) { pthread mutex lock(&compteur >mutex); return compteur >valeur ; } pthread mutex unlock(&compteur >mutex); //?? Impossible... Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 35 / 40
Moniteur Moniteur : exemple du compteur int compteur lire (COMPTEUR compteur) { int n; pthread mutex lock(&compteur >mutex); n= compteur >valeur ; pthread mutex unlock(&compteur >mutex); } return n; Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 36 / 40
Moniteur Moniteur : exemple void compteur ajouter(compteur compteur, int valeur ) { pthread mutex lock(&compteur >mutex); compteur >valeur= compteur >valeur + valeur ; pthread mutex unlock(&compteur >mutex); } Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 37 / 40
Moniteur Moniteur : conclusion très simple, évite d oublier de bloquer et de relacher le verrou, mais : exclusion mutuelle, ralentissement important du programme si le compteur est manipulé régulièrement. comment faire mieux?? Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 38 / 40
Conclusion 1 Introduction Théorie Création et destruction des threads 2 Synchronisation Section critique Variables de condition Moniteur Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 39 / 40
Conclusion À retenir Thread différences thread/processus Programmation multithread Synchronisation Section critique et exclusion mutuelle Utilisation des mutex et Variables de condition Moniteur. Problème des Producteurs/consommateurs, Lecteurs/rédacteurs... Interblocage. Fabien Rico (Univ. Claude Bernard Lyon 1) Processus légers séance 3 40 / 40