threads posix.1c/1003b ieee ieee présentation création
TRANSCRIPT
Threads Posix.1c/1003bThreads Posix.1c/1003bIEEEIEEE
Présentation
Création
PrésentationPré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 processusLe contexte processus
Contexte d'exécution•Registres généraux •Registres d'état•SP•PC
Contexte du noyau•Tables fichiers •Structure MV•Segment données
Pile
Tas
Données
code
Code données et pileContexte du processus
Le contexte processus : Détacher le flot des ressourcesLe contexte processus : Détacher le flot des ressources
Contexte du thread•Registres généraux •Registres d'état•SP•PC
Contexte du noyau•Tables fichiers •Structure MV•Segment données
Pile
Tas
Données
code
Code et données
Thread
Processus multi-threadProcessus multi-thread
Contexte du noyau•Tables fichiers •Structure MV•Segment données
Tas
Données
code
Code et données
Contexte du thread•Registres généraux •Registres d'état•SP2•PC2
Pile2
Thread 2
Contexte du thread•Registres généraux •Registres d'état•SP1•PC1
Pile1
Thread 1
La bibliothèque des threadsLa bibliothèque des threads
http://www.linux-kheops.com/doc/man/manfr/man-html-0.9/man3/pthread_create.3.html
Le manLe manNompthread_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); Descriptionpthread_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éeEn 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é. ErreursEAGAIN
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.
AuteurXavier Leroy <[email protected]> TraductionThierry Vignaud <[email protected]>, 2000 Voir Aussipthread_exit(3) , pthread_join(3) , pthread_detach(3) , pthread_attr_init(3) .
pthread.hpthread.h
ftp://sources.redhat.com/pub/pthreads-win32/dll-latest/include/
lien
Création d'un threadCré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(
1. pthread_t * pThread,
2. const pthread_attr_t * attr,
3. void* (*pFunction) (void*),
4. 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_createLe 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 Arguments
1. Le premier argument pthread_t * pThread, est un pointeur qui sera réalisé par la routine avec l'identifiant du nouveau thread.
2. 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.
3. 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é.
4. 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.
TerminaisonTerminaison
La terminaison a lieu lorsque
1. La fonction principale se termine.
2. 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
TerminaisonTerminaison
Prototype de la fonction
void pthread_exit(void * pReturnValue);
Récupération d'une valeur de retourRécupération d'une valeur de retour
Pour avoir la valeur de retour d'un thread terminé on utilise
int pthread_join(
1. pthread_t thread,
2. 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_joinLes 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 retourRécupération d'un entier comme code de retour
Pour conserver la portabilité d'un programme, la procédure a lieu en deux étapes
1. dans fonctionthread
int i=valretour;
pthread_exit((void*)i);
2. 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 principalInteraction avec le thread principal
Thread Principal
Fin pthread_create()Fin pthread_create()
pthread_join()pthread_join()
fin pthread_join()fin pthread_join()
exit()exit()
thread
ptread_exit()
pthread_create()pthread_create()
Synchronisation et communication de tâchesSynchronisation 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 mutuelleszones 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(
1. pthread_mutex_t * pMutex,
2. pthread_mutexattr_t * pAttributs
);
Pour détruire un mutex, nous utilisons la fonction suivante :
int pthread_mutex_destroy(pthread_mutex_t * pMutex);
ParamètresParamè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.
EmploiEmploi
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éverrouillageFonction 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 conditionelleAttente 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 conditionelleAttente conditionelle
Le principe repose sur deux fonctions de manipulation des conditionsint pthread_cond_wait(
pthread_cond_t * pCond,pthread_mutex_t * pMutex);
int pthread_cond_signal(pthread_cond_t * pCond);
Sans oublierint pthread_cond_init(
pthread_cond_t * pCond, pthread_condattr_t * pCandAttr);
ScénarioScé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.
Thread attendant la condition Thread signalant la condition
pthread_mutex_lock
pthread_cond_wait
déblocage du mutex
wait
pthread_mutex_lock
pthread_cond_signal
Réveil du thread
pthread_cond_wait
Tentative de récupérer le mutex
pthread_mutex_unlock
Fin de pthread_cond_wait
pthread_mutex_unlock
Fonction d'attente temporiséeFonction d'attente temporisée
Il faut préciser l'heure maximale et non la durée
int pthread_cond_timewait(
1. pthread_cond_t * pCond,
2. pthread_mutex_t * pMutex,
3. 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 threadAnnulation 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 nettoyageFonction 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(
1. void(*fonction)(void * argument),
2. void * argument
);
void pthread_cleanup_pop(int execution_routine);
OrdonnancementOrdonnancement
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 localOrdonnancement local
1
2
n
P1 P4 P12
Pi
Ti Tj
Le modèle d'ordonnancement est hiérarchiqueLes processus sont en concurrence par prioritéLe temps alloué à P4 est réparti aux taches
Ordonnancement globalOrdonnancement global
1
2
n
P1 P4 P12
Pi
Ti TjT1
T2
Les taches ou les processus sont ordonnancéesau même niveau
Ordonnancement MixteOrdonnancement Mixte
1
2
n
P1 P4 P12
Pi
Ti TjT1
T2
Avantages/inconvénientsAvantages/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'ordonnancementPolitique 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âcheLes 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 threadLes 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 XXXSignification 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 classiqueSCHED_RR pour un séquencement temps-réel avec l’algorithme Round RobinSCHED_FIFO pour un ordonnancement temps-réel FIFO.
Inheritsched signale si le thread a sa propre configuration d'ordonnancementPTHREAD_EXPLICIT_SCHED ordonnancement spécifique au threadPTHREAD_INHERIT_SCHED ordonnancement hérité
Liste complèteListe 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);
Norme POSIX.1003.bNorme POSIX.1003.b
liensliens
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/