Chapitre 2 Processus & Threads © Rim Moussa 2005-2006
Plan du chapitre Processus Threads Ordonnancement Communication interprocessus
Processus Un processus est une activité: programme, entrées, sorties, état Systèmes monoprocesseurs: pseudo-parallélisme Multiprogrammation: basculement entre processus Créer un processus? à l’initialisation du système, par un processus, requête utilisateur, job de trait par lots. UNIX: fork –création d’un clone et execve –modifier l’image du processus Windows: CreateProcess Processus Démons (daemon) ou en arrière-plan, exples: courriers électroniques, pages web… Premier-plan Les voir? Ctrl+alt+del sur Windows ps sur UNIX
… Processus Fin de Processus Hiérarchie de Processus Volontaire: arrêt normal ou arrêt pour erreur (le fichier à compiler n’existe pas) Involontaire: arrêt pour erreur fatale (division par 0) ou arrêté par un autre processus (Unix: kill, Windows: TerminateProcess) Hiérarchie de Processus Existe sous UNIX, arborescence de parent-enfants N’existe pas sous Windows, CreateProcess retourne un HANDLE utilisé pour contrôler le processus Implémentation de Processus Processus possède son propre espace d’adressage: programme, données, pile. Le changement de contexte (changement de processus) Table de processus, avec une entrée/ processus contenant registres, identificateur, ptr vers le segment texte, ptr vers segment de données, ptr vers le segment de pile, état …
… Etats de Processus En cours d’exécution (1) (2) (3) Prêt Bloqué (4) Le processus est bloqué, en attente d’une donnée, événement L’ordonnanceur choisit un autre processus L’ordonnanceur choisit ce processus La donnée devient disponible
Threads Un processus possède un espace d’adressage et un thread de contrôle unique. Un thread –processus léger ou leightweight process inclut: un compteur ordinal, qui effectue le suivi des instructions à exécuter des registres, qui détiennent les variables en cours. une pile, qui contient l’historique de l’exécution. Pour plus de performances, cas de threads liés aux E/Ss, d’autres de traitement Multithreading: +sieurs threads dans le même espace d’adressage. Etats d’un thread: en cours d’exécution, bloqué, prêt ou arrêté. Procédures de bibliothèque: thread_create, thread_exit, thread_wait, thread_yield (abandon volontaire de la CPU). Temps de création d’un processus >> Temps de création d’un thread (100 )
… Trait de Texte multithreaded Absbjshd Dnddjkjdq Hqdjlqdjl Jddmkm Djdlqjdjdq djdqkmkd Absbjshd Dnddjkjdq Hqdjlqdjl Jddmkm Djdlqjdjdq djdqkmkd Absbjshd Dnddjkjdq Hqdjlqdjl Jddmkm Djdlqjdjdq djdqkmkd clavier Disque noyau Thread 1: remet en forme le document Thread 2: interaction avec l’utilisateur Thread 3: écrit périodiquement le contenu de la RAM sur le disque
… Serveur Web multithreaded Les pages web les + consultées sont dans le cache. Le dispatcher lit les requêtes entrantes Le worker vérifie le cache, sinon effectue un read sur le disque, donc devient bloqué sur une E/S Dispatcher Thread Pool de worker threads Cache de pages web noyau Worker Dispatcher while (TRUE) { wait_for_work(&buf); look_for_page_in_cache(&buf, &page); if (page_not_in_cache(&page)) read_page_from_disk(&buf, &page); return_page(&page); } while (TRUE) { get_next_request(&buf); handoff_work(&buf); }
… Implémentation de Threads Soit dans l’Espace Utilisateur ou dans le Noyau Processus Thread Espace user Système d’exécution Table des threads noyau Table des processus Système d’exécution (runtime system): gére la table de threads, le basculement entre threads en cas d’implem en espace user est + rapide que celui en mode noyau. Implem en espace user: le processus a son propre algo d’ordonnancement entre les threads
Ordonnancement Ordonnanceur (scheduler): partie du SE qui sélectionne les processus. Algo d’ordonnancement (scheduling algorithm) ~ non-préemptif: sélectionne un processus, puis le laisse s’exécuter jusqu’à ce qu’il se bloque (E/S, wait) ou se termine. ~ préemptif: sélectionne un processus et le laisse s’exécuter pendant un quantum, préemption par l’interruption horloge Comportement de Processus: Processus de traitement Processus d’E/S
…Objectifs de l’ordonnancement Tous les systèmes Équité: attribuer à chaque processus un temps CPU équitable Équilibre: toutes les parties du système sont occupées Systèmes de traitement par lots Capacité de Trait: optimiser le #jobs/ heure Délai de rotation: réduire le délai entre la soumission et l’achèvement Taux d’utilisation du processeur Systèmes interactifs Temps de réponse: réponse rapide aux requêtes Systèmes temps réel Respect des délais: éviter de perdre les données Prévisibilité: éviter la dégradation de la qualité
… Algo d’ordonnancement 1er arrivé, 1er servi (first come first served) Le job le plus court en premier (shortest job first) Délais d’exécution connus Shortest remaining time next Tourniquet (round robin) Quantum court: trop de changement de contexte Quantum long: dégradation temps de réponse aux requêtes interactives Par Priorités Prévenir contre les situations de « famine » en diminuant la priorité du processus à chaque interruption ou priorité égale à l’inverse de la fraction d’utilisation du dernier quantum... Par Tirage au Sort
… Ordonnancement à 3 niveaux Processeur Job entrant File d’attente Disque Ordonnanceur du processeur Ordonnanceur d’admission Ordonnanceur de mémoire Mémoire Principale O. d’admission: Mélange jobs E/S et CPU, shortest job first… O. de mémoire: processus en mémoire ou sur disque ? Niveau de multiprogrammation? O. du processeur: Sélection des processus prêts en mémoire celui qui va s’exécuter
Communication interprocessus Conditions de Concurrence Sections Critiques Exclusion Mutuelle Sommeil & Activation Sémaphores Mutex Moniteurs Echange de Messages Barrières
Conditions de Concurrence Conditions de concurrence (race conditions): situation où 2 processus ou plus effectuent des lectures et des écritures conflictuelles. Exemple du Spouleur d’impression Un processus qui veut imprimer un fichier, entre son nom dans un répertoire de spoule Le processus démon d’impression regarde périodiquement s’il y a des fichiers à imprimer. Il a 2 variables: in: pointe vers la prochaine entrée libre. out: pointe vers le prochain fichier à imprimer in = 7, out = 4 A et B deux processus qui veulent imprimer un fichier A >> lire in, next_free_slot = 7 Interruption: la CPU bascule vers le processus B B >> lire in, next_free_slot = 7, entrée7 = fichierB, in = 8 A >> entrée7 = fichierA, in = 8 Problème: le fichierB ne sera pas imprimé
… Conditions de Concurrence Comment éviter les conditions de concurrence? Solution: Interdire que plusieurs processus lisent et écrivent des données partagées simultanément. Exclusion Mutuelle: permet d’assurer que si un processus utilise une variable ou fichier partagés, les autres processus seront exclus de la même activité
Les Sections Critiques les Sections Critiques, méthode d’exclusion mutuelle A entre dans sa section critique A quitte sa section critique A B quitte sa section critique B t1 t2 t3 t4 B tente d’entrer dans sa section critique B entre dans sa section critique
L’Exclusion Mutuelle avec Attente Active (busy waiting) Désactivation des interruptions Après son entrée dans une SC, un processus désactive les interruptions, puis les réactive Il empêche ainsi l’horloge d’envoyer des interruptions et le processeur de basculer Il est imprudent de permettre à des processus user de désactiver les interruptions Variables de verrou (lock) Avant d’entrer en SC, tester la valeur de verrou, si verrou = 0, verrou 1, entrer en SC Défaillance: 2 processus peuvent entrer simultanément dans leurs sections critiques comme le spouler d’impression Alternance Stricte la variable turn porte le numéro du processus dont c’est le tour d’entrer en SC. Chaque processus inspecte la valeur de la variable, avant d’entrer en SC. Inconvénient: consomme bcp de temps CPU
… Exclusion Mutuelle avec Attente Active (busy waiting) … Alternance Stricte while (TRUE) { while (TRUE) { while (turn != 0); while (turn != 1); critical_region(); critical_region(); turn = 1; turn = 0; non_critical_region(); non_critical_region(); } } Les attentes actives sont performantes dans le cas où elles sont brèves. En effet, il y’ a risque d’attente P0 quitte la CS, turn = 1 P1 termine sa CS, turn = 0 Les 2 processus sont en section non critique P0 exécute sa boucle, quitte la SC et turn = 1 P0 quoiqu’il a terminé, il ne peut pas entrer en SC, il est bloqué
… Exclusion Mutuelle avec Attente Active (busy waiting) Solution de Peterson: combinaison de variables verrou, de variables d’avertissement sans alternance stricte. #define FALSE 0 #define TRUE 1 #define N 2 // nombre de processus int turn; // à qui le tour? int interested[N]; // initialement les valeurs sont false void enter_region (int process) { int other; other = 1 – process; interested[process] = TRUE; turn = process; while (turn == process && interested[other] == TRUE); } void leave_region (int process) { interested[process] = FALSE;
… Exclusion Mutuelle avec Attente Active (busy waiting) Instruction TSL solution qui nécessite de l’aide du matériel. TSL acronyme de Test and Set Lock Elle consiste en la lecture du contenu du mot mémoire Lock du registre RX, puis stocker d’une valeur différente de 0 à l’adresse mémoire lock. Ces opérations sont indivisibles: le processeur exécutant TSL verrouille le bus mémoire. enter_region: TSL REGISTER, LOCK | copie lock dans le registre et la définit à 1 CMP REGISTER, #0 | tester si lock est égale à 0 JNE enter_region | si elle est différente de 0 boucle RET | retour à l’appelant, entre en SC leave_region: MOVE LOCK, #0 | stocke un 0 dans lock RET | retour à l’appelant
Problème du consommateur-producteur Connu aussi sous le nom de tampon délimité (bounded buffer). 2 processus partagent un tampon commun de taille fixe N. Le producteur place des jetons dans le tampon. Le consommateur récupère un jeton Problème 1: le producteur souhaite placer un nouveau jeton alors que le tampon est plein. Problème 2: le consommateur souhaite récupérer un jeton alors que le tampon est vide Count dénote le nombre de jetons dans le tampon
Sommeil & Activation Problème du producteur-consommateur Ne consomme pas de la CPU comme l’attente active Primitives de communication: sleep: appel système qui provoque le blocage de l’appelant wake up: appel système qui réveille un processus. Problème du producteur-consommateur void producer (void) { int item; while (TRUE) { item = produce_item(); if (count == N) sleep(); insert_item(item); count++; if (count == 1) wakeup(consumer); } void consumer (void) { if (count == 0) sleep(); item = remove_item(); count--; if (count == N-1) wakeup(producer); consume_item(item); #define N 100 int count = 0;
… Sommeil & Activation Problème de blocage: Le consommateur note que le tampon est vide Interruption: arrêt du consommateur sans qu’il parte en sommeil Le producteur insère un jeton, incrémente le décompte, appelle wakeup pour réveiller le consommateur Le signal wakeup est perdu, car le consommateur n’est pas en sommeil Le consommateur reprend, pour lui le tampon est vide, il dort Le producteur remplit le tampon et dort Solution: ajouter un bit d’attente d’éveil. Quand un wakeup est envoyé à un processus le bit est à 1; le consommateur teste le bit, s’il est à 1, il le remet à 0 et reste en éveil Cette solution est + difficile à généraliser en cas de + sieurs processus.
Sémaphores Sémaphore: # de wakeup enegistrés 2 opérations: down: si la valeur de la variable sémaphore est > 0, la décrémenter; si la valeur est nulle: sleep up: incrémenter la valeur de la sémaphore et réveiller au hasard un processus dormant down et up sont implémentées en tant que appels système, le SE désactive les interruptions, et c’est ainsi que les opérations de vérification, de màj et de sommeil s’exécutent de manière atomique Problème du producteur-consommateur 3 sémaphores: full, empty et mutex full: # d’emplacemens occupés dans le tampon empty: # d’emplacements vides dans le tampon mutex: pour s’assurer que le consommateur et le producteur n’accèdent pas simultanément au tampon
… Sémaphores #define N 100 void consumer (void) { Typedef int semaphore; semaphore mutex = 1; semaphore empty = N; semaphore full = 0; void producer (void) { int item; message m; while (TRUE) { item = produce_item(); down(&empty); down(&mutex); insert_item(item); up(&mutex); up(&full); } void consumer (void) { int item; while (TRUE) { down(&full); down(&mutex); item = remove_item(&m); up(&mutex); up(&empty); consume_item(item); }
Mutex Sémaphore d’exclusion mutuelle, où la fonction décompte n’est pas nécessaire. Une mutex a 2 états: déverrouillé: valeur 0 ou verrouillé Si +sieurs threads sont bloqués sur une mutex, l’un d’entre eux est choisi au hasard pour prendre possession du verrou. mutex_lock et mutex_unlock mutex_lock: TSL REGISTER, MUTEX | copie mutex dans le registre et la définit à 1 CMP REGISTER, #0 | tester si mutex est égale à 0 JSE ok | si elle est différente de 0 retour CALL thread_yield | relâcher la CPU ok: RET | retour à l’appelant mutex_unlock: MOVE MUTEX, #0 | stocke un 0 dans mutex ! Comparer au code d’enter_region slide 17
Moniteurs Collection de procédures, de variables, de structures de données regroupées dans un module. Un processus ne peut appeler que les procédures du moniteur. Les moniteurs sont des constructions de langage, et c’est au compilateur d’implanter l’exclusion mutuelle sur les entrées du moniteur. Problème du producteur-consommateur 2 variables conditionnelles (vc): full et empty Quand une procédure du moniteur découvre qu’elle ne peut plus poursuivre, elle effectue un wait sur une vc Si un signal est adressé une vc attendue, un processus est réactivé.
… Moniteurs monitor ProducteurConsommateur condition full, empty; integer count; procedure insert (item: integer) begin if count = N then wait(full) insert_item(item); count++; if (count = 1) then signal(empty); end; function remove: integer if count = 0 then wait(empty); remove = remove_item; count--; if (count = N –1) then signal(full); count = 0; end monitor; procedure producteur begin while true do { item = produce_item; ProducteurConsommateur.insert(item); } end; procedure consommateur item = ProducteurConsommateur.remove; consume_item(item);
Echange de Messages Convient aux systèmes distribués 2 primitives appels systèmes: send et receive send (destination, &message) receive (source, &message) Problèmes Perte de messages dans le réseau, + de fiabilité par les ACKs, la retransmission de messages et la détection de doublons Problème du producteur-consommateur Les messages émis et non encore reçus sont mis en mémoire par le SE Le consommateur envoie N messages vides au producteur Le producteur remplit chaque message vide reçu
…Echange de Messages #define N 100 void producer (void) { int item; message m; while (TRUE) { item = produce_item(); receive(consumer, &m); build_message(&m, item); send(consumer, &m); } void consumer (void) { int item, i; for(i=0; i<N;i++) send(producer, &m); receive(producer,&m); item = extract_item(&m); send(producer, &m); consume_item(item); Problème, si le producer travaille + vite que le consumer le producer envoie tous les messages pleins et reste bloqué en attente d’un message vide.
Les Barrières Mécanisme de synchronisation destiné aux groupes de processus. Cas d’applications décomposées en phases, où aucun processus ne peut entrer dans la phase suivante tant que tous les processus ne sont pas prêts à y entrer. Solution: placer une barrière, et quand un processus atteint la barrière il est bloqué jusqu’à ce que tous les processus atteignent la barrière. Barrière Barrière Barrière A A A B B B C C C