Université Pierre et Marie Curie

Systèmes d'exploitation des ordinateurs

Chapitre 1. Les systèmes d'exploitation
Chapitre 2. Mécanismes d'exécution et de communication
2.1. Notions sur les procédures
2.2. Notions sur les interruptions
2.3. Programmation des entrées-sorties
2.4. Les processus sous Unix
2.4.1. Principes généraux
2.4.2. Réalisation
2.4.3. Les threads
Chapitre 3. Gestion des activités parallèles
Chapitre 4. Gestion des fichiers
Chapitre 5. Partage des ressources
Chapitre 6. Au-dessus du système d'exploitation
Chapitre 7. Notions sur les communications
Chapitre 8. Notions sur la sécurité
Bibliographie
Chapitre 9. Exercices et TPs
Examens
Page d'accueilTable des matièresNiveau supérieurPage précédenteBas de la pagePage suivante

2.4.3. Les threads

Un processus définit l’ensemble des ressources nécessaires à l’exécution d’un programme. Plusieurs processus peuvent, comme nous l’expliquerons plus en détails au chapitre V, exécuter le même code mais ils disposent chacun d’un espace d’adressage et d’autres ressources propres nécessaires à leur exécution.

L’apparition des machines multi-processeurs, le dévelopement d’applications client-serveur dans lesquelles une machine doit répondre à de nombreuses requêtes, ont montré les limites de ce modèle. Il est difficile de faire coopérer des processus indépendants : chacun dispose de ses ressources propres indépendantes, protégées par le moniteur, contre toute tentative d'accès aux zones mémoire qui lui correspondent par les autres proicessus. Faire communiquer des processus nécessite donc de recourir à des techniques sophistiquées qui font appel à des bibliothèques spécialisées. Ceci est compliqué, couteux et consommateur de ressources.

On a donc imaginé la possibilité de découper un processus en sous processus ou threads. Tous les threads partagent le même environnement que le processus dont ils sont issus et exécutent chacun la tache pour laquelle ils ont été programmés. Les threads fils et leur père partagent toutes leurs données : modifier la valeur d'une variable dans l'un la modifie pour tous les autres. La meilleure image que l'on puisse en donner est celle de fonctions qui partageraient une zone de mémoire commune.

L'emploi des threads s'est encore accru dans les processeurs modernes multi-coeurs qui sont capables de parallèliser des séries d'instructions. Il est évident que lorsqu'on en vient à paralléliser le fonctionnement au niveau de petits groupes d'instructions que seule la notion de threads peut s'appliquer, pas celle de rpocessus coopératifs pour lesquels tout le gain obtenu par le parallèlisme serait perdu dans le changement de contexte.

On peut schématiser le partage des ressources entre processus et threads comme indiqué sur la figure 3.15.

dd

figure 3.15 : schéma du partage des ressources entre un processus et ses threads

Parmi les données communes au processus père et à ses et aux threads, on peut citer :

  • le répertoire de travail
  • l’implantation en mémoire
  • l’ensemble des descripteurs …
  • les droits du processus et son propriétaire (ceci sera explicité au chapitre IV).

Le processus père comme chaque thread possède en propre :

  • ses registres
  • ses piles d’exécution
  • ses signaux de communication
  • l’ensemble des tables et variables nécessaires à gérer son avancement

Exemple

/*     Exemple de thread sous Unix  */


#include <stdio.h>
#include <pthread.h>

/* 
   déclaration ci-dessous pour les structures  qui récupèrent les tid
   La dimension 3 s'explique car on veut créer 3 threads
*/
void mon_thread(int );
pthread_t pthread_id[3];
main()
{
   int i;
   for (i=0;i<3;i++)
   {
/*
   Création de threads. Arguments :
   - identification du thread : structure de type pthread_t 
     (3 dans et exemple)
   - attributs : NULL par défaut
   - pointeur sur la fonction à exécuter
   - pointeurs sur la liste des arguments de la fonction

Warning à la compilation car on devrait passer un pointeur sur 
l'argument de la fonction mon_thread. Mais comme chaque thread est 
susceptible de modifier les arguments de la fonction à exécuter et 
que leur ordre d'exécution ne peut être connu ni systématiquement 
le même, on risque de se retrouver avec une variable i modifiée 
aléatoirement dans un thread, et donc avec une valeur incohérente 
pour les autres threads. Cela provient du fait que les threads 
partagent le même espace de données. Au contraire si on passe les 
arguments des fonctions par valeur chaque instance crée un espace 
particulier qui évite ce phénomène.
*/
   
      if(pthread_create(pthread_id+i, NULL, 
           (void *)mon_thread,i)  <0)
          fprintf(stderr,"Erreur création thread %d\n",i);
      }
      fprintf(stdout,"thread initial pid %d tid %d\n",getpid(), 
          pthread_self());
}
/*  fonction exécutée par chaque thread
*/
void mon_thread(int i)
{
    fprintf(stdout,"Thread n° %d, pid %d, tid %d\n",i, getpid(), 
       pthread_self());
    sleep(2);
}

Copyright Yves Epelboin, université P.M. Curie 1998, MAJ 4 avril, 2005

Page d'accueilTable des matièresNiveau supérieurPage précédenteHaut de la pagePage suivante