Académique Documents
Professionnel Documents
Culture Documents
Réalisé par :
GHANOUCH Issam
ES-SLASSI RAZZOUKI Mehdi
Système d’exploitation 2
#include <pthread.h>
#include <stdio.h>
int main () {
pthread_t thread_id;
while (1)
fputc ('o', stderr);
return 0;
}
La succession des ‘x’ et des ‘o’ sur l’écran est imprévisible car le système
d’exploitation (Linux) passe la main alternativement aux deux threads.
Pour la fonction de thread print_xs, il se termine avec return NULL. Pour la
terminaison explicite du thread On peut la remplacer par l’appel de la fonction
pthread_exit(NULL).
#include <pthread.h>
#include <stdio.h>
Struct char_print_parms {
/* Caractère à afficher. */
Char character ;
/* Nombre de fois où il doit être affiché. */
Int count ;
};
int main () {
pthread_t thread1_id;
pthread_t thread2_id;
thread1_args.character = 'x';
thread1_args.count = 30000;
pthread_create (&thread1_id, NULL, &char_print, &thread1_args);
thread2_args.character = ‘o’;
thread2_args.count = 20000;
pthread_create (&thread2_id, NULL, &char_print, &thread2_args);
Return 0 ;
}
Soit un programme ait une série de tâches en attente traitées par plusieurs
threads concurrents.
Fonction de Thread Traitant une File de Tâches job-queue1.c :
#include <malloc.h>
#include<pthread.h>
struct job {
struct job* next;
int jobe;
};
Système d’exploitation 6
Il faut penser de toute sorte que les opérations soit atomique et non
interrompu.
Donnant l’exemple de notre programme : Il faut vérifier la valeur de
job_queue et si elle n'est pas NULL, supprimer la première tâche, tout cela
en une seule opération atomique.
Système d’exploitation 9
3-2- MUTEXES :
Les MUTEX (Exclusion Mutuelle) est une solution pour rendre des opérations
atomiques,
Pour protéger les concurrences d’accès aux ressources critiques. Donc le
MUTEX est un verrouillage spécial, si un thread a verrouillé un MUTEX et un
2éme vient pour verrouiller le même MUTEX, il reste bloqué ou suspendu
jusqu‘à ce que l’autre thread quitte la section critique.
Fonction de Thread Traitant une File de Tâches en utilisant les MUTEX job-queue1.c:
void* thread_function ()
pthread_mutex_lock (&job_queue_mutex);
job_queue = job_queue->next;
pthread_mutex_unlock (&job_queue_mutex);
/* Traite la tâche. */
process_job (next_job);
free (next_job);
pthread_exit(NULL);
Avec le mécanisme du MUTEX, nous avons fait en sorte à ce que les problèmes
de segmentation ne surviennent plus !
Thread 1 thread 2
Pthread_mutex_unlock(&mutex2) Pthread_mutex_unlock(&mutex1)
Pthread_mutex_unlock(&mutex1) Pthread_mutex_unlock(&mutex2)
Les mutex est une méthode puissante qui permet de protéger un morceau de
code accédant à une ressource critique, mais ne sont pas très efficaces dans certains
cas.
Par exemple on suppose que les 2 threads, chargés du traitement des tâches
stockées dans la liste chainée, terminent l’exécution de toutes les tâches. Dans ce cas,
ils se terminent systématiquement et si d’autres tâches arrivent elles ne seront pas
traitées.
Donc on va utiliser les sémaphores qui permettent d’endormir les threads
jusqu’à l’arrivée de nouvelles tâches.
Pour les utiliser on a besoin de définir un sémaphore sem_t job_queue_count
et de l’initialiser avec sem_init (&job_queue_count, 0, 0).
Au début de la fonction du thread nous avons appelé la fonction sem_wait et
après avoir ajouté une tâche dans la file on appele la fonction sem_post.
#include <malloc.h>
#include<pthread.h>
#include <semaphore.h>
sem_t job_queue_count;
pthread_mutex_t job_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
struct job {
/* Champ de chaînage. */
struct job* next;
/* Autres champs décrivant la tâche... */
int jobe ;
};
/* Liste chaînée de tâches en attente. */
struct job* job_queue = NULL;
Système d’exploitation 14
}
void* thread_function (void* arg)
{
while (1) {
/* Récupère la tâche suivante. */
sem_wait (&job_queue_count);
pthread_mutex_lock (&job_queue_mutex);
struct job* next_job = job_queue;
/* Supprime cette tâche de la liste. */
//n=0 ? sleep(0) : sleep(1) ;
job_queue = job_queue->next;
process_job (next_job);
pthread_mutex_unlock (&job_queue_mutex);
/* Traite la tâche. */
free (next_job);
}
pthread_exit(NULL);
Système d’exploitation 15
void Remplir_Struct(int a)
{
int main()
Système d’exploitation 16
{
int i;
pthread_t thread1 , thread2 ;
sem_init (&job_queue_count, 0, 0);
sleep(2);
sleep(2);
pthread_join(thread1 ,NULL) ;
pthread_join(thread2 , NULL ) ;
return 0 ;
}
Système d’exploitation 17
Ainsi, nous avons réussi à mettre en œuvre un mécanisme pour que les threads
chargés du traitement des taches ne se terminent jamais. Si aucune tâche n'est
disponible pendant un certain temps, les threads sont simplement bloqués dans
sem_wait (&job_queue_count).
Après avoir ajouté une tâche à la file, elle envoie un signal de réveil au
sémaphore, indiquant qu'une nouvelle tâche est disponible par sem_post
(&job_queue_count).
Variable de condition :
Une variable de condition vous permet de spécifier une condition qui lorsqu'elle
est remplie autorise l'exécution du thread et inversement, une condition qui lorsqu'elle
est remplie bloque le thread. Du moment que tous les threads susceptibles de modifier
la condition utilisent la variable de condition correctement, Linux garantit que les
threads bloqués à cause de la condition seront débloqués lorsque la condition change.
Fonctions pour manipuler les variables de conditions :
-pthread_cond_init : initialise une variable de condition. Le premier argument
est un pointeur vers une variable pthead_cond_t. Le second argument, un
pointeur vers un objet d'attributs de variable de condition, est ignoré par
GNU/Linux. Le mutex doit être initialisé à part, comme indiqué dans la Section
4.4.2, « MUTEXES ».
-pthread_cond_signal : valide une variable de condition. Un seul des threads
bloqués sur la variable de condition est débloqué. Si aucun thread n'est bloqué
sur la variable de condition, le signal est ignoré. L'argument est un pointeur vers
la variable pthread_cond_t. Un appel similaire, pthread_cond_broadcast,
débloque tous les threads bloqués sur une variable de condition, au lieu d'un
seul.
-pthread_cond_wait : bloque l'appelant jusqu'à ce que la variable de condition
soit validée.
L'argument est un pointeur vers la variable pthread_cond_t. Le second
argument est un pointeur vers la variable pthread_mutex_t. Lorsque
pthread_cond_wait est appelée, le mutex doit déjà être verrouillé par le thread
appelant. Cette fonction déverrouille automatiquement le mutex et se met en attente
sur la variable de condition. Lorsque la variable de condition est validée et que le
thread appelant est débloqué, pthread_cond_wait réacquiert automatiquement un
verrou sur le mutex.
Implémentation Simple de Variable de Condition spin-condvar.c
#include <pthread.h>
int thread_flag;
pthread_mutex_t thread_flag_mutex;
void initialize_flag ()
{
pthread_mutex_init (&thread_flag_mutex, NULL);
thread_flag = 0;
}
/* Appelle do_work de façon répétée tant que l'indicateur est actif ; sinon,
tourne dans la boucle. */
void* thread_function (void* thread_arg)
{
Système d’exploitation 19
while (1) {
int flag_is_set;
/* Protège l'indicateur avec un mutex. */
pthread_mutex_lock (&thread_flag_mutex);
flag_is_set = thread_flag;
pthread_mutex_unlock (&thread_flag_mutex);
if (flag_is_set)
do_work ();
/* Rien à faire sinon, à part boucler. */
}
Return NULL ;
}
/* Positionne la valeur de l'indicateur de thread à FLAG_VALUE. */
Void set_thread_flag (int flag_value)
{
/* Protège l'indicateur avec un verrouillage de mutex. */
pthread_mutex_lock (&thread_flag_mutex);
thread_flag = flag_value;
pthread_mutex_unlock (&thread_flag_mutex);
}
Dans cet exemple, on une fonction de thread qui exécute une boucle infinie,
accomplissant une tâche à chaque itération. La boucle du thread, cependant, a besoin
d'être contrôlée par un indicateur : la boucle ne s'exécute que lorsqu'il est actif ; dans
le cas contraire, la boucle est mise en pause.
#include <pthread.h>
int thread_flag;
pthread_mutex_t thread_flag_mutex;
void initialize_flag ()
{
pthread_mutex_init (&thread_flag_mutex, NULL);
thread_flag = 0;
}
/* Appelle do_work de façon répétée tant que l'indicateur est actif ; sinon,
tourne dans la boucle. */
void* thread_function (void* thread_arg)
{
while (1) {
int flag_is_set;
/* Protège l'indicateur avec un mutex. */
Système d’exploitation 20
pthread_mutex_lock (&thread_flag_mutex);
flag_is_set = thread_flag;
pthread_mutex_unlock (&thread_flag_mutex);
if (flag_is_set)
do_work ();
/* Rien à faire sinon, à part boucler. */
}
Return NULL;
}
/* Positionne la valeur de l'indicateur de thread à FLAG_VALUE. */
Void set_thread_flag (int flag_value)
{
/* Protège l'indicateur avec un verrouillage de mutex. */
pthread_mutex_lock (&thread_flag_mutex);
thread_flag = flag_value;
pthread_mutex_unlock (&thread_flag_mutex);
}
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void* thread_function (void* arg)
{
fprintf (stderr, "L'identifiant du thread fils est %d\n", (int) getpid ());
/* Boucle indéfiniment. */
while (1);
return NULL;
}
int main ()
{
pthread_t thread;
fprintf (stderr, "L'identifiant du thread principal est %d\n",(int)getpid());
pthread_create (&thread, NULL, &thread_function, NULL);
/* Boucle indéfiniment. */
while (1);
return 0;
}
Explication :
Le programme thread-pid du Listing threadpid le démontre. Le programme crée un
thread ; le thread original et le nouveau appellent tous deux la fonction getpid et
affichent leurs identifiants de processus respectifs, puis bouclent indéfiniment.
Système d’exploitation 22
Exécution :
Gestion des signaux est basée sur le fait que chaque thread a le même
comportement d’un signal c’est à dire identifier par un pid, et comme chaque signal
ne concerne qu’un seul processus (en passant en paramètre le pid). ceci offre un
mécanisme d’échange entre les threads ( et les processus en général )
5- Comparaison Processus/Threads :
Dans ce tableau on présente les différences entre les processus et les threads
Thread Processus
-Les threads d'un programme doivent -Un processus fils, au contraire, peut
exécuter le même code exécuter un programme différent en
-Un thread peut endommager les utilisant une fonction exec.
données d'autres threads du même -Chaque processus dispose de sa
processus car les threads partagent le propre copie de l'espace mémoire du
même espace mémoire et leurs programme.
ressources. -Les processus devraient être utilisés
-Les programmes qui ont besoin d'un pour des programmes ayant besoin
parallélisme finement contrôlé d'un parallélisme plus grossier.
-Le partage de données entre des -Le partage de données entre des
threads est trivial car ceux-ci partagent processus nécessite l'utilisation de
le même espace mémoire. mécanismes IPC