Vous êtes sur la page 1sur 26

Gestion des processus

Création des processus légers (threads)

Mr: EZZATI

1
Plan
 Introduction sur les Threads
 Threads en mode utilisateur et threads en mode noyau
 Implémentation des threads sous linux
 Création des threads
 Synchronisation des threads
 Les identifiants des threads
 Les attributs des threads
 Threads joingnables et threads détachés
 Annulation des threads
 Section critiques des threads
 Gestion des signaux sur les threads
 l’Appel Système Clone
 Conclusion
 Introduction
Les threads appelés aussi « processus légers » sont des

mécanismes permettant à un programme de faire plus
d'une chose à la fois, ils sont similaires aux processus
puisqu’ils représentent tous les deux l’exécution d’un
ensemble d’instructions du langage machine d’un
processeur .
Du point de vue de l’utilisateur ,ces exécutions semblent
se dérouler en parallèle . Toutefois chaque processus
possède ses propres ressources (fichiers, mémoires, etc.),
les processus légers appartenant au même processus
père partagent une grande partie de leurs ressources.
 Les threads en mode (utilisateur & noyau )
 Les threads en mode utilisateur
Les threads en mode utilisateur vivent entièrement à l’intérieur d’un programme et

de ses bibliothèques. Dans ce modèle, le Système d’exploitation ne sait rien


des threads. Pour ce qu’il en voit, votre processus utilisant les threads n’est qu’un
processus comme les autres.

 C’est la façon la plus facile d’implémenter les threads. Mais il y a un gros

désavantage, puisque si un des threads bloque, ils bloquent tous au sein du


processus qui les a lancés. Le noyau constante seulement qu’un processus a bloqué.
Parmi les activités bloquantes courantes, on trouve la plupart des appels système,
des E/S, et des commandes comme sleep().
 Les threads en mode (utilisateur & noyau )
Les threads du noyau
 Les threads du noyau sont l’étape suivante dans l’évolution des threads.
Le Système d’Exploitation connaît des threads gérés par le noyau et en tient
compte. La principale différence entre un thread du noyau et un thread en mode
utilisateur est le blocage

 Avec les threads du noyau, un thread peut bloquer sans que les
autres threads bloquent. Ce n’est pas le cas avec les threads en mode
utilisateur, où le noyau bloque au niveau du processus et pas au niveau
du thread.

 C’est un grand pas en avant, qui peut donner à un programme utilisant


les threads un bonus de performance sur les programmes ne les utilisant pas.
Les threads qui bloquent au cours d’E/S, par exemple, ne bloqueront pas
les threads qui font autre chose.
 Les threads en mode (utilisateur & noyau )
Les threads multiprocesseurs du noyau sont l’étape
finale dans l’évolution du support pour les threads.
Avec ce modèle, sur une machine à plusieurs
processeurs, le Système d’exploitation peut faire tourner
simultanément plusieurs threads sur plusieurs
processeurs.
 Cela peut améliorer sérieusement les performances
d’un programme utilisant les threads, puisque plusieurs
s’exécuteront en même temps. En retour, cependant,
tous les problèmes de synchronisation qui ne se
manifestaient pas avec les threads du noyau vont
surgir
Processus Unix/Linux
 Threads au sein d’un processus
Unix/Linux
 Implémentation des Threads sous GNU/Linux

Nous avons vu comment un programme peut créer un


processus fils avec fork(). Celui-ci exécute
immédiatement le programme de son père, la mémoire
virtuelle, les descripteurs de fichiers, etc. de son père
étant copiés.

 Le processus fils peut modifier sa mémoire, fermer les


descripteurs de fichiers sans que cela affecte son père,
et vice versa.
Implémentation des Threads sous GNU/Linux
 Lorsqu'un programme crée un nouveau thread, par contre, rien
n'est copié. Le thread créateur et le thread créé partagent tous deux
le même espace mémoire, les mêmes descripteurs de fichiers et
autres ressources

 Si un thread modifie la valeur d'une variable, par exemple, l'autre


thread verra la valeur modifiée. De même, si un thread ferme un
descripteur de fichier, les autres threads ne peuvent plus lire ou
écrire dans ce fichier.
 Comme un processus et tous ses threads ne peuvent exécuter qu'un
seul programme à la fois, si un thread au sein d'un processus appelle
une des fonctions exec*(), tous les autres threads se terminent.
 Implémentation des Threads sous GNU/Linux
 Lancez le programme en arrière-plan puis invoquez ps x pour
afficher vos processus en cours d'exécution. Voici à quoi la sortie
pourrait ressembler:
 Création de Threads

Chaque thread d'un processus est caractérisé par un identifiant de


thread. Lorsque vous manipulez les identifiants de threads dans des
programmes C ou C++, veillez à utiliser le type pthread_t.
Exemple : pthread_t thread_id;

Lors de sa création, chaque thread exécute une fonction de thread,


il s'agit d'une fonction ordinaire contenant le code que doit
exécuter le thread .

 Lorsque la fonction se termine, le thread se termine également.


Sous GNU/Linux, les fonctions de thread ne prennent qu'un seul
paramètre de type void* et ont un type de retour void*.
 void* print_xs (void* arg) {/* le code que doit exécuter le thread */}
 Création de Threads
 La fonction pthread_create crée un nouveau thread. Voici les
paramètres dont elle a besoin ,voyons l’exemple suivant:
 pthread_create (&thread_id, NULL, &print_xs, NULL);

 L'argument de la fonction thread est une méthode pratique


pour passer des données à un thread. Comme son type
est void*, cependant, vous ne pouvez pas passer beaucoup de
données directement en l'utilisant. Une technique couramment
utilisée est de définir une structure de données pour chaque
argument de thread.
 Une solution possible de faire attendre un thread à la fin d’un autre
 Synchroniser des Threads
est d’utiliser une fonction similaire à wait(&retour), qui attend la fin
d'un thread au lieu de celle d'un processus.

 Cette fonction est pthread_join(id_t, &retour),qui prend deux


arguments: l'identifiant du thread à attendre et un pointeur vers une
variable void* qui recevra la valeur de retour du thread s'étant
terminé.

 Si le second argument que vous passez à pthread_join n'est


pas NULL, la valeur de retour du thread sera stockée à
l'emplacement pointé par cet argument.
 Les Identifiants de Thread
 La fonction pthread_self renvoie l'identifiant du thread depuis
lequel elle a été appelée elle est équivalente à getpid() . Cet
identifiant de thread peut être comparé à un autre en utilisant la
fonction pthread_equal.
 Ces fonctions peuvent être utiles pour déterminer si un identifiant de
thread particulier correspond au thread courant.

 Par exemple, un thread ne doit jamais appeler pthread_join pour


s'attendre lui-même (dans ce cas, pthread_join renverrait le code
d'erreur EDEADLK).
 if(!pthread_equal(pthread_self(),other_thread))
pthread_join (other_thread, NULL);
 Les Attributs de Thread
Les attributs de thread proposent un mécanisme pour contrôler
finement le comportement de threads individuels.
Si vous passez un pointeur nul, les attributs par défaut sont utilisés
pour configurer le nouveau thread.

Pour indiquer des attributs de thread personnalisés ,on doit suivre les
étapes suivants:

 Créez un objet pthread_attr_t . La façon la plus simple de le faire est


de déclarer une variable automatique de ce type.
 pthread_attr_t attr ;

 Appelez pthread_attr_init en lui passant un pointeur vers cet objet.


Cela initialise les attributs à leur valeur par défaut.
 pthread_attr_init (&attr);
 Les Attributs de Thread
 Modifiez l'objet d'attributs pour qu'ils contiennent les valeurs
désirées. pthread_attr_setdetachstate à voir plu tard;

 Passez un pointeur vers l'objet créé lors de l'appel


à pthread_create.
 pthread_create (&thread, &attr, &thread_function, NULL);

 Appelez pthread_attr_destroy pour libérer l'objet d'attributs.


La variable pthread_attr_t n'est pas libérée en elle-même; elle
peut être réinitialisée avec pthread_attr_init.
 pthread_attr_destroy (&attr);
 Thread joignable ou détaché
 Les ressources d'un thread joignable, comme pour un processus,
ne sont pas automatiquement libérées par GNU/Linux lorsqu'il se
termine. Au lieu de cela, l'état de sortie du thread reste dans le
système (un peu comme pour un processus zombie) jusqu'à ce
qu'un autre thread appelle pthread_join pour récupérer cette valeur
de retour.

 Les ressources d'un thread détaché, au contraire, sont


automatiquement libérées lorsqu'il se termine. Cela impliquer qu'un
autre thread ne peut attendre qu'il se termine avec pthread_join ou
obtenir sa valeur de retour.

 Pour paramétrer l'état de détachement dans un objet d'attributs de


thread, utilisez
 pthread_attr_setdetachstate (&att , etat);
 Annulation de Thread
Dans des circonstances normales, un thread se termine soit en
arrivant à la fin de la fonction de thread, soit en appelant la
fonction pthread_exit.

Pour annuler un thread, appelez pthread_cancel, en lui


passant l'identifiant du thread à annuler.

 Un thread annulé peut être joint ultérieurement; en fait, vous


devriez joindre un thread annulé pour libérer ses ressources, à
moins que le thread ne soit détaché La valeur de retour d'un
thread annulé est la valeur spéciale
PTHREAD_CANCELED.
 Annulation de Thread
 Un thread peut se comporter de trois façons suivantes
face à l'annulation :
 Il peut être annulable de façon asynchrone.
C'est-à-dire à n'importe quel moment de son exécution.
 Il peut être annulable de façon synchrone.
le thread n'est annulé que lors qu'il atteint un certain point
de son exécution. Lors de sa création par exemple.
 Il peut être impossible à annuler.
Les tentatives d'annulation sont ignorées silencieusement.
 Un thread peut désactiver son annulation avec la
fonction pthread_setcancelstate.

 Le premier argument est PTHREAD_CANCEL_DISABLE


Pour désactiver l'annulation , ou PTHREAD_CANCEL_ENABLE
pour réactiver l'annulation.

 Le second argument, s'il n'est pas NULL, pointe vers


une variable qui recevra l'état précédent de l'annulation.
Cet appel, par exemple, désactive l'annulation du
thread appelant.
 Gestion de Signaux
 Le comportement de l'interaction entre les threads et les signaux varie
d'un type d'UNIX à l'autre. Sous Linux, ce comportement est dicté par
le fait que les threads sont implémentés comme des processus.

 Comme chaque thread est un processus distinct, et comme un signal


est délivré à un processus particulier, il n'y a pas d'ambiguïté au
niveau du thread qui va recevoir le signal. Typiquement, les
signaux envoyés de l'extérieur du programme sont envoyés au
processus correspondant au thread principal du programme.

 Au sein d'un programme multithread, il est possible pour un thread


d'envoyer un signal à un autre thread bien défini. Utilisez la
fonction pthread_kill pour cela. Son premier paramètre est
l'identifiant du thread et le second, le numéro du signal.
 L'appel Système clone
 L'appel système clone de Linux est une forme hybride entre fork
et pthread_create , il permet à l'appelant de spécifier les ressources
partagées entre lui et le nouveau processus.

 clone nécessite également que vous spécifiiez la région mémoire


utilisée pour la pile d'exécution du nouveau processus.

 Bien que nous mentionnions clone pour satisfaire la curiosité du


lecteur, cet appel système ne doit pas être utilisé au sein de
programmes. Utilisez fork pour créer des nouveaux processus et
pthread_create pour créer des nouveaux threads.
 Conclusion
 Les threads devraient être utilisés pour les programmes
qui ont besoin d'un parallélisme finement contrôlé.

 Les processus devraient être utilisés pour des programmes


ayant besoin d'un parallélisme plus grossier.

 Le partage de données entre des threads est trivial car


ceux-ci partagent le même espace mémoire. Le partage de
données entre des processus nécessite l'utilisation de
mécanisme IPC (Inter Process Communication).
int pthread_getconcurrency(void);
int pthread_setconcurrency(int new_level);
Comment terminer un processus ?

Merci pour votre attention

Vous aimerez peut-être aussi