Professional Documents
Culture Documents
Hiver 2013
Threads POSIX
Cration Attente de la fin dun fil d'excution Terminaison Nettoyage la terminaison
Chapitre 4 - 2
Le modle processus dcrit prcdemment est un programme qui sexcute selon un chemin unique (compteur ordinal). On dit quil a un fil d'excution ou flot de contrle unique (single thread). De nombreux systmes dexploitation modernes offrent la possibilit dassocier un mme processus plusieurs chemins dexcution (multithreading).
Processus et threads
1
Noyau d'un systme d'exploitation Gnie informatique et gnie logiciel Ecole Polytechnique de Montral
Chapitre 4 - 3
Un thread est une unit d'excution rattache un processus, charge d'excuter une partie du programme du processus. Un processus est vu comme tant un ensemble de ressources (espace dadressage, fichiers, priphriques) que ses fils d'excution (threads) partagent. Lorsquun processus est cr, un seul fil dexcution (thread) est associ au processus. Ce fil peut en crer dautres. Chaque fil a: un identificateur unique une pile d'excution des registres (un compteur ordinal) un tat
Chapitre 4 - 4
Le multithreading permet lexcution simultane ou en pseudo-parallle de plusieurs parties dun mme processus.
Chapitre 4 - 5
Ractivit (le processus peut continuer sexcuter mme si certaines de ses parties sont bloques), Partage de ressources (facilite la coopration, amliore les performances), conomie despace mmoire et de temps. Il faut moins de temps pour : crer, terminer un fil (sous Solaris, la cration dun processus est 30 fois plus lente que celle dun thread), Changer de contexte entre deux threads dun mme processus.
Chapitre 4 - 6
Processeur Intel i5, cycle de 0.4ns, LMBench Temps ns 65 170 138 778 205 1492 515 7022 1348 158 22716
Gnie informatique et gnie logiciel Ecole Polytechnique de Montral
Opration syscall trivial read write stat fstat open/close select 10 fd select 500 fd signal segfault dlai de tuyau
Opration fork+exit fork+execve pthread_create changer de processus changer de threads iadd imul idiv dadd ddiv
Temps ns 263000 269400 ~19000 ~1600 ~1400 .2 .18 9.52 1.32 12.05
Chapitre 4 - 7
Un thread pour interagir avec lutilisateur, Un thread pour reformater en arrire plan, Un thread pour sauvegarder priodiquement le document
Noyau d'un systme d'exploitation Gnie informatique et gnie logiciel Ecole Polytechnique de Montral
Chapitre 4 - 8
Chapitre 4 - 9
Chapitre 4 - 10
Chapitre 4 - 11
Les threads utilisateur sont gnralement crs, et grs rapidement. Ils facilitent la portabilit (comparativement aux autres implmentations) Inconvnients :
tout instant, au plus un fil par processus est en cours dexcution. Cette implmentation nest pas intressante pour des systmes multiprocesseurs. Si un thread dun processus se bloque, tout le processus est bloqu. Pour pallier cet inconvnient, certaines librairies transforment les appels systme bloquants en appels systme non bloquants.
Chapitre 4 - 12
Les threads Java sont grs par la machine virtuelle Java (JVM).
Chapitre 4 - 13
Java Thread
import java. awt.*; class Travailleur extends Thread { public void run() { System. out. println(" Je suis le thread Travailleur"); try { Thread. sleep( 1000); } catch (InterruptedException e){} System. out. println(" Je suis le thread Travailleur"); } } public class Principal { public static void main( String args[]) { Travailleur t = new Travailleur(); t. start(); System. out. println(" Je suis le thread principal"); try { Thread. sleep( 1000); } catch (InterruptedException e){} System. out. println(" Je suis le thread principal"); } }
Gnie informatique et gnie logiciel Ecole Polytechnique de Montral
Chapitre 4 - 14
Les threads noyau sont directement supports par le systme dexploitation. Le systme dexploitation se charge de leur gestion. Du temps CPU est allou chaque thread. (modle un--un) Si un thread dun processus est bloqu, un autre thread du mme processus peut tre lu par le noyau Cette implmentation est plus intressante pour les systmes multiprocesseurs. Un processus peut ajuster les niveaux de priorit de ses threads. Par exemple, un processus peut amliorer son interactivit en assignant:
une forte priorit un thread qui traite les requtes des utilisateurs et une plus faible priorit aux autres.
Chapitre 4 - 15
Performance (gestion plus coteuse) Les programmes utilisant les fils d'excution noyau sont moins portables que ceux qui utilisent des fils en mode utilisateur.
Chapitre 4 - 16
Linux ne fait pas de distinction entre les processus et les fils d'excution qui sont communment appels tches. Il implmente le modle multiflot un--un. La cration de tches est ralise au moyen de lappel systme fork, vfork ou clone.
Chapitre 4 - 17
vfork() fonctionne comme fork() sauf que le processus parent est bloqu jusqu ce que le processus cr appelle exit() ou exec(). Clone() permet de spcifier les ressources partager (espace dadressage, fichiers, signaux..) entre les tches cratrice et cre.
Activ Nouveau fil Partager umask, /, CWD Partager descripteurs fd Partager raction aux SIG Conserver le PID
Non activ Nouveau processus Ne pas partager Copier les fd Copier la table Fil obtient son propre PID
Chapitre 4 - 18
Un ensemble de fils dun mme processus peuvent tre associs un fil noyau (pooling thread).
Chapitre 4 - 19
Solaris 2 implmente le multiflot selon le modle plusieurs--plusieurs: fils utilisateur (user-level thread), processus lgers qui sont des fils noyau (LWP), et processus. Il fournit une bibliothque contenant des API pour la cration et la gestion de threads utilisateur et des LWP. La bibliothque se charge de: assigner un ou plusieurs LWP un processus, multiplexer les threads utilisateur avec les LWP disponibles pour le processus. ajuster dynamiquement le nombre de LWP fournis au processus : Si tous les LWP sont bloqus et un thread est prt lexcution, un autre LWP sera fourni au processus pour excuter ce thread. Les LWP inutiliss pendant un certain temps sont limins Chaque LWP supporte un ou plusieurs threads utilisateur et correspond un thread noyau. Le modle plusieurs--plusieurs a t abandonn, dans Solaris 8, au profit du modle un--un.
Chapitre 4 - 20
1 - thread_process.c
static uint64_t a = 0; int main(int argc, char **argv) { int p, i; for (i = 0; i < 2; i++) { if ((p = fork()) < 0) return EXIT_FAILURE; if (p == 0) { count(&a); printf("child %d a=%" PRId64 "\n", getpid(), a); return EXIT_SUCCESS; } } for (i = 0; i < 2; i++) { wait(NULL); } printf("a=%" PRId64 "\n", a); return EXIT_SUCCESS; } #define MAX 1000000000 void *count(void *arg) { volatile uint64_t *var = (uint64_t *) arg; volatile uint64_t i; for (i = 0; i < MAX; i++) { *var = *var + 1; } return NULL; }
2 - thread_pthread.c
static uint64_t a = 0; int main(int argc, char **argv) { int p, i; pthread_t t1, t2; pthread_create(&t1, NULL, count, &a); pthread_create(&t2, NULL, count, &a); pthread_join(t1, NULL); pthread_join(t2, NULL); printf("pid=%d a=%" PRId64 "\n", getpid(), a); return EXIT_SUCCESS; }
3 - thread_userspace.c
static uint64_t a = 0; int main(int argc, char **argv) { pth_init(); pth_t t1, t2; t1 = pth_spawn(PTH_ATTR_DEFAULT, count, &a); t2 = pth_spawn(PTH_ATTR_DEFAULT, count, &a); pth_join(t1, NULL); pth_join(t2, NULL); printf("a=%" PRId64 "\n", a); return EXIT_SUCCESS; }
Chapitre 4 - 21
tat bloqu
Excution parallle
Chapitre 4 - 22
valuation de la performance
zhukov$ time ./module2/thread_process child 23088 a=1000000000 child 23087 a=1000000000 a=0 real 0m2.507s user 0m4.900s sys 0m0.040s zhukov$ time ./module2/thread_pthread pid=23090 a=1074274083 real 0m3.791s user 0m7.240s sys 0m0.040s zhukov$ time ./module2/thread_userspace a=2000000000 real 0m4.679s user 0m4.660s sys 0m0.000s
Copies indpendantes Le calcul se fait en parallle real user / 2 Rsultat non-dterministe! Pourquoi pthread est plus lent que process ici? Le rsultat semble dterministe ici? real user
Chapitre 4 - 23
Lobjectif premier des Pthreads est la portabilit (disponibles sous Solaris, Linux, Windows XP ).
Description Crer un nouveau fil d'excution Terminer le fil appelant Attendre la fin d'un autre fil Crer un mutex Dtruire un mutex Verrouiller un mutex Relcher un mutex Crer une condition Dtruire une condition Attendre aprs une condition Signaler une condition
Gnie informatique et gnie logiciel Ecole Polytechnique de Montral
Appel pthread_create pthread_exit pthread_join pthread_mutex_init pthread_mutex_destroy pthread_mutex_lock pthread_mutex_unlock pthread_cond_init pthread_cond_destroy pthread_cond_wait pthread_cond_signal
Noyau d'un systme d'exploitation
Chapitre 4 - 24
Fonction pthread_create()
int pthread_create(
pthread_t *tid, // sert rcuprer le TID du pthread cr const pthread_attr_t *attr, // sert prciser les attributs du pthread `(taille de la pile, priorit.) //attr = NULL pour les attributs par dfaut void * (*func) (void*), // est la fonction excuter par le pthread void *arg); //le paramtre de la fonction.
L'appel renvoie 0 s'il russit, sinon il renvoie une valeur non nulle identifiant l'erreur qui s'est produite
Gnie informatique et gnie logiciel Ecole Polytechnique de Montral
Chapitre 4 - 25
Attend la fin dun pthread. Lquivalent de waitpid des processus sauf quon doit spcifier le tid du pthread attendre. status sert rcuprer la valeur de retour et ltat de terminaison.
pthread_t pthread_self(void);
Chapitre 4 - 26
Fonction pthread_exit()
void pthread_exit( void * status); Termine l'excution du pthread
Si le pthread nest pas dtach, le TID du pthread et ltat de terminaison sont sauvegards pour les communiquer au pthread qui effectuera pthread_join. Un pthread dtach (par la fonction pthread_detach(pthread_t tid)) a pour effet de le rendre indpendant de celui qui l'a cr (pas de valeur de retour attendue). Un pthread peut annuler ou terminer lexcution dun autre pthread (pthread_cancel). Cependant, les ressources utilises (fichiers, allocations dynamiques, verrous, etc) ne sont pas libres. Il est possible de spcifier une ou plusieurs fonctions de nettoyage excuter la terminaison du pthread (pthread_cleanup_push() et pthread_cleanup_pop()).
Gnie informatique et gnie logiciel Ecole Polytechnique de Montral
Chapitre 4 - 27
Exemple pthread
// exemple_pthreads.c #define _REENTRANT #include <pthread.h> #include <unistd.h> #include <stdio.h> void afficher(int n, char lettre) { int i,j; for (j=1; j<n; j++) { for (i=1; i < 10000000; i++); printf("%c",lettre); fflush(stdout); } } void *threadA(void *inutilise) { afficher(100,'A'); printf("\n Fin du thread A\n"); fflush(stdout); pthread_exit(NULL); }
Noyau d'un systme d'exploitation Gnie informatique et gnie logiciel Ecole Polytechnique de Montral
Chapitre 4 - 28
Chapitre 4 - 29
int main() { int i; pthread_t thA, thB; printf("Creation du thread A"); pthread_create(&thA, NULL, threadA, NULL); pthread_create(&thB, NULL, threadB, NULL); sleep(1); //attendre la fin des threads printf("Le thread principal attend que les autres se terminent\n"); pthread_join(thA,NULL); pthread_join(thB,NULL); exit(0); }
Noyau d'un systme d'exploitation Gnie informatique et gnie logiciel Ecole Polytechnique de Montral
Chapitre 4 - 30
Thread B Thread A
Thread C
pthread_join pthread_exit
pthread_exit
pthread_join
Noyau d'un systme d'exploitation
pthread_exit
Gnie informatique et gnie logiciel Ecole Polytechnique de Montral
Chapitre 4 - 31
Chapitre 4 - 32
Chapitre 4 - 33
Chapitre 4 - 34
Chapitre 4 - 35
Chapitre 4 - 36
Chapitre 4 - 37
Gnie informatique et gnie logiciel Gnie Informatique et Genie Logiciel cole Polytechnique de Montral Ecole Polytechnique de Montral
for (int i=0; i < argc-1; ++i) { // attendre les threads if ( pthread_join(thread_id[i], (void *) &retour) > 0){ cerr << "Echec de lattente des threads" << endl; return 3; } cout << endl << "Thread " << thread_id[i] << " retourne " << *retour; } cout << endl << "Termine" << endl; return 0; // fin de main() }
Chapitre 4 - 39
-bash-3.2$ g++ p11_1.cpp -lpthread -o p11_1 -bash-3.2$ ./p11_1 A B C Affichage A sera affiche C sera affiche 4 fois.4 fois. B sera affiche 4 fois. ACBACBACBAC Thread B 3085867920 retourne 4 Thread 3075378064 retourne 4 Thread 3064888208 retourne 4 Termine -bash-3.2$ ./p11_1 A B C Affichage C sera affiche B sera affiche 3 fois. 3 fois. A sera affiche 2 fois. BCABCA Thread 3086285712 retourne 2B C Thread 3075795856 retourne 3 Thread 3065306000 retourne 3 Termine Chapitre 4 - 40
pthread_cancel(th); // annulation
void *mon_thread(void *inutilise) -bash-3.2$ gcc cancel1.c -lpthread -o cancel1 { FILE* un_fichier; int i; -bash-3.2$ ./cancel1 setvbuf(stdout, (char *) NULL, _IONBF, 0); 0123456789 if ((un_fichier = fopen("sortie.txt","w")) == NULL) fclose perror("Erreur dans l'ouverture du fichier"); unlink else { // pour une terminaison propre pthread_cleanup_push(_unlink, (void *) "sortie.txt"); pthread_cleanup_push(_fclose, (void *) un_fichier); for (i=0; i<10000; i++){ fprintf(un_fichier,"%d",i); fprintf(stdout,"%d",i); } pthread_cleanup_pop(1); // excute fclose pthread_cleanup_pop(1); // excute unlink } } Gnie informatique et gnie logiciel Noyau d'un systme d'exploitation Chapitre 4 - 41 Ecole Polytechnique de Montral
Programmation multi-fil
Accs normal aux variables locales. Variables globales par thread qui peuvent tre accdes normalement. La mmoire alloue dynamiquement et les variables globales peuvent tre partages. Tous les accs des variables partages doivent tre protgs par un mcanisme de synchronisation qui assure la cohrence. Les fonctions de librairies doivent elles aussi tre prvues pour la programmation multi-fil.
Chapitre 4 - 42