400 likes | 561 Views
Threads Posix.1c/1003b IEEE. Présentation Création. Présentation. Un thread est un déroulement d'un programme qui s'exécute parallèlement à d'autres unités en cours d'exécution.
E N D
Threads Posix.1c/1003bIEEE Présentation Création
Présentation • Un thread est un déroulement d'un programme qui s'exécute parallèlement à d'autres unités en cours d'exécution. • Les différents threads d'une application partagent un même espace d'adressage. L'accès concurrentiel aux mêmes données nécessite une synchronisation pour éviter les interférences. • En outre chaque thread dispose d'une pile et d'un contexte d'exécution contenant les registres du processeur et un compteur d'instruction. • L'idée générale est de détacher le flot d'exécution des ressources
Le contexte processus Contexte du processus Code données et pile • Contexte d'exécution • Registres généraux • Registres d'état • SP • PC Pile Tas • Contexte du noyau • Tables fichiers • Structure MV • Segment données Données code
Le contexte processus : Détacher le flot des ressources Code et données Thread Pile Tas • Contexte du thread • Registres généraux • Registres d'état • SP • PC Données code • Contexte du noyau • Tables fichiers • Structure MV • Segment données
Processus multi-thread Code et données Thread 1 Thread 2 Pile1 Pile2 • Contexte du thread • Registres généraux • Registres d'état • SP1 • PC1 • Contexte du thread • Registres généraux • Registres d'état • SP2 • PC2 Tas Données code • Contexte du noyau • Tables fichiers • Structure MV • Segment données
La bibliothèque des threads http://www.linux-kheops.com/doc/man/manfr/man-html-0.9/man3/pthread_create.3.html
Le man • Nom • pthread_create - créé un nouveau thread • Synopsis • #include <pthread.h> • int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg); • Description • pthread_create créé un nouveau thread s'éxécutant concurremment avec le thread appelant. Le nouveau thread exécute la fonction start_routine en lui passant arg comme premier argument. Le nouveau thread s'achève soit explicitement en appelant pthread_exit(3) , ou implicitement lorsque la fonction start_routine s'achève. Ce dernier cas est équivalent à appelér pthread_exit(3) avec la valeur renvoyée par start_routine comme code de sortie. L'argument attr indique les attributs du nouveau thread. Voir pthread_attr_init(3) pour une liste complète des attributs. L'argument attr peut être NULL, auquel cas, les attributs par défaut sont utilisés: le thread créé est joignable (non détaché) et utilise la politique d'ordonnancement usuelle (pas temps-réél). • Valeur Renvoyée • En cas de succès, l'identifiant du nouveau thread est stocké à l'emplacement mémoire pointé par l'argument thread, et 0 est renvoyé. En cas d'erreur, un code d'errur non nul est renvoyé. • Erreurs • EAGAIN • pas assez de ressources système pour créer un processus pour le nouveau thread. • EAGAIN • il y a déjà plus de PTHREAD_THREADS_MAX threads actifs. • Auteur • Xavier Leroy <Xavier.Leroy@inria.fr> • Traduction • Thierry Vignaud <tvignaud@mandrakesoft.com>, 2000 • Voir Aussi • pthread_exit(3) , pthread_join(3) , pthread_detach(3) , pthread_attr_init(3) .
pthread.h • ftp://sources.redhat.com/pub/pthreads-win32/dll-latest/include/ • lien
Création d'un thread • Un type opaque est utilisé pour distinguer les threads d'une application • unsigned long pthread_t(dans la bibliothèque LinuxThreads) • Création d'un thread • int pthread_create( • pthread_t * pThread, • const pthread_attr_t * attr, • void* (*pFunction) (void*), • void * pArg ); • Cette routine renvoie 0 si elle a réussi, sinon elle renvoie un numéro de lerreur survenue. • pthread_create ne remplit pas la var errno.
Le code C de thread_create /* Thread creation */ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void *), void *arg) { pthread_descr self = thread_self(); struct pthread_request request; if (__pthread_manager_request < 0) { if (pthread_initialize_manager() < 0) return EAGAIN; } request.req_thread = self; request.req_kind = REQ_CREATE; request.req_args.create.attr = attr; request.req_args.create.fn = start_routine; request.req_args.create.arg = arg; sigprocmask(SIG_SETMASK, (const sigset_t *) NULL, &request.req_args.create.mask); __libc_write(__pthread_manager_request, (char *) &request, sizeof(request)); suspend(self); if (self->p_retcode == 0) *thread = (pthread_t) self->p_retval; return self->p_retcode; }
Arguments • Le premier argument pthread_t * pThread, est un pointeur qui sera réalisé par la routine avec l'identifiant du nouveau thread. • le second argument const pthread_attr_t * attr, correspond aux attributs dont on desire doter le nouveau thread. Par défaut, le thread reçoit des attributs standard. • Le troisième argument void* (*pFunction) (void*), est un pointeur représentant la fonction principale du nouveau thread (c'est ce qu'il doit faire). Dès la création du thread, la fonction est invoquée en recevant comme argument le pointeur passé en dernière position. Le prototype de cette fonction est imposé et ne peut donc pas être modifié. • Le quatrième argument void * pArg est de type void *, il sera ainsi transformé dans le type de l'argument que l'on veur passer au thread. Cet argument est généralement un numéro permettant d'indiquer le travail à réaliser.
Terminaison • La terminaison a lieu lorsque • La fonction principale se termine. • Par l'appel de la fonction pthread_exit • le thread est alors éliminé • par l'appel de pthread_exit toutes les fonctions de nettoyage sont invoquées
Terminaison • Prototype de la fonction • void pthread_exit(void * pReturnValue);
Récupération d'une valeur de retour • Pour avoir la valeur de retour d'un thread terminé on utilise • int pthread_join( • pthread_t thread, • void ** pPtrReturnValue ); • La fonction peut échouer si le thread attendu n'existe pas ; s'il est détaché ou si le thread demande sa propre fin
Les argument de pthread_join • L'argument pthread_t thread, est le thread qui se termine • Le deuxième argument void ** pPtrReturnValue est un pointeur sur un void * utilisé pour recevoir la valeur de retour du thread. Cette valeur de retour est spécifiée a la fin du thread grâce a la fonction pthread_exit. Ce dernier argument peut être NULL si nous ne sommes pas intéressé par la valeur de retour du thread.
Récupération d'un entier comme code de retour • Pour conserver la portabilité d'un programme, la procédure a lieu en deux étapes • dans fonctionthread int i=valretour; pthread_exit((void*)i); • dans le main par exemple void * retour; pthread_join(threadid, & retour); • Ainsi, il faut utiliser d'abord un pointeur void * temporaire, qu'on transforme ensuite en int.
Interaction avec le thread principal pthread_create() thread ptread_exit() Thread Principal Fin pthread_create() pthread_join() fin pthread_join() exit()
Synchronisation et communication de tâches Dans POSIX, les outils sont plus rapides et plus puissants que les IPC • Sémaphore binaire • Les mutex sont dédiés à l'exclusion mutuelle. • Les attributs des mutex permettent de choisir le protocole de gestion de ressource associé • Sémaphores en lecture/écriture • POSIX.1c propose les sémaphore en lecture/écriture • Aucun protocole de gestion de ressources • Variable conditionnelle • Utilisée avec un mutex pour créer des moniteurs • On peut utiliser une attente bornée qui ne sera jamais signalée. • RDV • Après la création d'un RDV synchronisé des tâches peuvent s'attendre mutuellement
zones d'exclusions mutuelles La synchronisation est un des enjeux essentiels du développement d'application multithreads entre les différents fils d'exécution concurrents. Pour accéder à des données communes, il est indispensable de mettre en œuvre un mécanisme d'exclusion mutuelle des threads. Cas des mutex • Pour créer un mutex, la librairie POSIX fournie la fonction suivante :int pthread_mutex_init( • pthread_mutex_t * pMutex, • pthread_mutexattr_t * pAttributs ); • Pour détruire un mutex, nous utilisons la fonction suivante :int pthread_mutex_destroy(pthread_mutex_t * pMutex);
Paramètres • Le premier paramètre est un pointeur sur un pthread_mutex_t destiné a recevoir le descripteur du mutex nouvellement créé. • Le second paramètre pAttributs est utilisé pour paramétrer le mutex, cette variable regroupe tous les attributs du mutex, les paramètres par défaut sont obtenus avec un pointeur Null.
Emploi • La fonction pthread_mutex_init est employée comme ceci pthread_mutex mutex pthread_mutexattr_t muxeattr /*initialisationdemutexattr*/ /*initialisationdumutex*/ if ((mutex=malloc (sizeof(pthread_mutex_t))==Null) return (-1); pthread_mutex_init(&mutex,&mutexattr);
Fonction de verrouillage et déverrouillage • Si le mutex est libre, il peut être immédiatement verrouillé et attribué au thread appelant. Si le mutex est déjà maintenu par un autre thread, la fonction reste bloquée (indéfiniment). Ce n'est pas un point d'annulation. • int pthread_mutex_lock(pthread_mutex_t * pMutex); • Dès que le thread a fini de travailler avec les données protégées, le mutex doit être déverrouillé pour laisser la place a d’autres thread.Ceci est simplement réalisé en appelant la fonction suivante : • int pthread_mutex_unlock(pthread_mutex_t * pMutex);
Attente conditionelle • Lorsqu'un processus attend qu'un événement survienne on emploie une technique de synchronisation a base de variable conditionnelle. • Un thread attend une condition ; il est avertit par un autre thread lorsqu'elle est réalisée. • Lorsqu’un variable de condition n’est plus utilisée, il faut la libérée avec la fonction suivante :int pthread_cond_destroy(pthread_cond_t * pCond);
Attente conditionelle • Le principe repose sur deux fonctions de manipulation des conditions • int pthread_cond_wait( pthread_cond_t * pCond, pthread_mutex_t * pMutex ); • int pthread_cond_signal(pthread_cond_t * pCond); • Sans oublier int pthread_cond_init( pthread_cond_t * pCond, pthread_condattr_t * pCandAttr );
Scénario • Toutes les variables de condition ont besoin d’être associé à un mutex spécifique qui va bloquer le thread jusqu'à l’émission de la notification.
Fonction d'attente temporisée • Il faut préciser l'heure maximale et non la durée • int pthread_cond_timewait( • pthread_cond_t * pCond, • pthread_mutex_t * pMutex, • struct timespec * expiration ); • pthread_cond_wait a le comportement d'un point d'annulation. Lorsqu'un thread reçoit une demande d'annulation durant cette fonction d'attente, elle se termine mais doit récupérer avant le mutex associé à la condition. Cela signifie qu'elle peut bloquer indéfiniment avant de se terminer.
Annulation d'un thread • L'annulation doit thread doit laisser les données dans un état prévisible, et le seul état prévisible du mutex associé à un appel pthread_cond_wait() est le verrouillage. Bien sur, le thread ne doit pas laisser le mutex bloqué. On utilise une fonction de nettoyage.
Fonction de nettoyage • Lorsqu'un thread s'attribue une ressource qui nécessite une libération ultérieure, il enregistre le nom de la routine de libération dans une pile spéciale. Lorsque le thread se termine, les routines sont dépilées et exécutées • void pthread_cleanup_push( • void(*fonction)(void * argument), • void * argument ); • void pthread_cleanup_pop(int execution_routine);
Ordonnancement • Deux niveaux de programmation concurrente sont offert dans Posix • les processus et les threads • L'ordonnancement peut se faire à deux niveaux • local et global
Ordonnancement local Ti Tj P1 P4 P12 Le modèle d'ordonnancement est hiérarchique Les processus sont en concurrence par priorité Le temps alloué à P4 est réparti aux taches Pi
Ordonnancement global T2 P1 P4 P12 Ti Tj T1 Les taches ou les processus sont ordonnancées au même niveau Pi
Ordonnancement Mixte T2 P1 P4 P12 Ti Tj T1 Pi
Avantages/inconvénients • Dans un ordonnancement local si une tâche fait une action bloquante le processus est bloqué. • Les tâches d'un même processus ne peuvent être ordonnancées sur un multiprocesseurs • Pour modifier dans un ordonnancement global un paramètre influençant l'ordonnancement il faut faire un appel système plus lourd qu'un appel de fonction interne au processus. • Les tâches d'un même processeurs peuvent être ordonnancées sur plusieurs processeurs
Politique d'ordonnancement • SCHED_FIFO • La tâche prête (processus) qui a la plus forte priorité au niveau global et qui est arrivée la première • SCHED_RR • fonctionne comme SCHED_FIFO avec un quantum de temps appliqué par tourniquet • SCHED_OTHER • défini par l'implémentation • SCHED_SPORADIC • La politique de base est la politique SCHED_FIFO, avec une quantité de temps alloué à un serveur dont la capacité est cette quantité
Les attributs d'une tâche • Les attributs d'une tâches peuvent définir • Le type d'ordonnancement à appliquer • La taille de la pile • Une priorité • La définition d'un serveur sporadique associé à la tâche • L'état attaché ou détaché de la tâche • Après la création d'un tâche, il est possible de modifier les attributs d'une tâche.
Les attributs de thread • Création et destruction : • int pthread_attr_init ( pthread_attr_t * ) • int pthread_attr_destroy (pthread_attr_t * ) • Lecture : • int pthread_attr_getXXX (const pthread_attr_t *, T*) • Mise à jour : • int pthread_attr_setXXX ( pthread_attr_t *, T ) XXX représente le nom de l'attribut.
Signification et valeur de XXX detachstate : spécifie s’il sera possible de se synchroniser sur le fil d’exécution une fois qu’il sera terminé. PTHREAD_CREATE_JOINABLE pour autoriser la synchronisation PTHREAD_CREATE_DETACHED pour la décliner. schedpolicy correspond à la politique d’ordonnancement employée par le thread. SCHED_OTHER pour l’ordonnancement classique SCHED_RR pour un séquencement temps-réel avec l’algorithme Round Robin SCHED_FIFO pour un ordonnancement temps-réel FIFO. Inheritsched signale si le thread a sa propre configuration d'ordonnancement PTHREAD_EXPLICIT_SCHED ordonnancement spécifique au thread PTHREAD_INHERIT_SCHED ordonnancement hérité
Liste complète int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy); int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param); int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit); int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit); int pthread_attr_setscope(pthread_attr_t *attr, int scope); int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
liens • http://old.devpaks.org/show.php?devpak=18 • http://sources.redhat.com/pthreads-win32/ • http://www.humanfactor.com/pthreads/ • http://www.llnl.gov/computing/tutorials/pthreads/