[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[LONG] Re: Multi xxx (suite: Compatibilite materiel.)



On Sun, 5 Dec 1999, Yaneric Roussel wrote:

> 
> 
> 
> >From: Martin Quinson <mquinson@zeppelin-cb.de>
> > > Ai-je bien compris? Si oui, pourquoi y-a-t-il cette différence entre les
> > > threads utilisateurs et systèmes?
> >
> >Ben la réponse est dans la question : c'est à l'utilisateur >d'ordonnancer 
> >les threads utilisateurs. Le systeme ne sait meme pas >qu'ils existent. 
> >Comment pourait il les migrer d'un processeur sur >l'autre ?
> 
> Ok, d'accord, mais alors comment Linux fait-il pour savoir qu'un processus 
> système possède plusieurs threads et qu'il peut alors les partager sur plus 
> d'un processeur?
> 
> Si, lorsque l'on parle de processus systèmes, on parle seulement du noyau, 
> je comprends que le noyau a pu être bâtit pour cela, mais si ça inclu tout 
> ce qui est autour, je ne voie pas pourquoi Linux pourrais prendre la 
> décision de distribuer sur plus d'un processeur des threads provenant d'un 
> processus système et ne pas être capable de faire la même chose pour un 
> processus utilisateur...

J'ai l'impression qu'il y a quelques confusions entre les divers noms
employés. Je vais essayer d'expliquer les choses comme je les comprends.

  Tout d'abord, le noyau Linux.

  Au niveau de l'ordonnancement (le scheduler), le noyau linux ne manipule
que des "kernel threads". Ces threads sont identifiés dans le noyau par
une structure de données du type "struct task_struct". Chaque "kernel
thread" a, en particulier, un pid qui l'identifie et permet de lui envoyer
des signaux.
  Quand le shell appelle fork() pour lancer un processus standard, le
noyau va créer un nouveau "kernel thread". Chaque fois que le scheduler
donne la main à ce "kernel thread", cela signifie que le processus
s'exécute.
  Le scheduler du noyau linux ordonnance les "kernel threads" sur tous les
processeurs à sa disposition. Avec un noyau standard (sans patch
particulier), un "kernel thread" peut être ordonnancé (exécuté) sur
n'importe quel processeur disponible, et le noyau peut très bien le
changer de processeur chaque fois qu'il lui donne la main.


  Au niveau utilisateur.

  Dans ce contexte, le mot "thread" change de sens. Il signifie alors "fil
d'exécution dans un processus". Un thread possède en propre ses registres
processeurs (PC, SP, registres de données, ...). Il partage avec les
autres threads dans un processus, entre autres, la table des fichiers
ouverts et la mémoire.
  On distingue plusieurs type de tels threads suivant la façon dont ils
sont ordonnancés, les deux plus importants étant les threads utilisateurs 
et les threads noyaux.

Les threads utilisateurs.
  Les threads utilisateurs sont des threads gérés par le processus
lui-même (souvent à l'aide d'une bibliothèque). Le noyau n'a pas
connaissance de ces threads, il ne voit qu'une seule entité. Pour linux,
cela signifie qu'il n'y a qu'un seul "kernel thread" qui s'occupe de tous
ces threads utilisateurs. 
 Avantages : 
  * efficacité (il n'y a pas besoin de faire d'appels systèmes pour les
primitives relatives aux threads (création, synchronisation), seulement
des appels de fonctions à la bibliothèque de threads utilisée),
 * flexibilité (la bibliothèque peut gérer ses threads comme bon lui
semble et mettre en place des comportement spécifiques (exemple: migration
de threads d'une machine à une autre dans PM2))
 Inconvénients :
  * On ne bénéficie pas des SMP (le scheduler noyau n'oronnance le "kernel
thread" que sur un seul processeur à un instant donné)
  * Appels bloquants : lorsqu'un thread fait un appel bloquant (lecture
sur une socket par exemple), le noyau suspend le "kernel thread" jusqu'à
completion de l'appel système, empêchant d'autres threads utilisateurs de
s'exécuter, même s'ils sont prêts.

Les threads noyaux.
  Dans ce cas, c'est l'OS qui mets à la disposition de l'utilisateur des
primitives pour gérer les threads. La situation de linux est un peu
particulière à ce sujet.
  Linux met à disposition de l'utilisateur la primitive clone(). C'est une
version plus puissante que fork() (en fait, fork() appel clone() avec les
bons flags). clone() crée un nouveau "kernel thread" qui peut (suivant les
flags passés en paramètre) partager ou non différents aspects avec le
"kernel thread" faisant l'appel. Le nouveau "kernel thread" a
nécessairement de nouveaux registres processeurs, un nouveau pid, par
contre, il peut partager ou non avec l'ancien la table de fichiers
ouverts, l'espace d'adressage (la mémoire), ...
  Xavier Leroy a utiliser cet appel système pour construire autour une
bibliothèque de threads noyaux : c'est la bibliothèque que l'on a
l'habitude d'utiliser sous linux (libpthread). La chose un peu étrange
sous linux, c'est que ces threads ont un pid différent (alors qu'ils
devraient partager celui du processus)
 Avantages :
  * utilisation des SMP : chaque thread s'exécute dans un "kernel
thread" différent qui peuvent être ordonnancés par le noyau chacun sur un
processeur différent en même temps.
  * Appels bloquant : quand un thread fait un appel bloquant, il ne bloque
que son "kernel thread", les autres threads continuent de s'exécuter dans
leur "kernel thread".
 Inconvénients :
  * efficacité : le passage en mode noyau (appel système) pour toutes les
primitives de création/synchronisation/... est coûteux
  * flexibilité : on ne peut faire avec ce genre de threads que ce qui est
prévu dans le noyau.

Autres types de threads.
  On trouve également d'autres types de bibliothèque de threads, en
particulier des bibliothèques dites "mixte" ou "à deux niveau" où quelques
threads noyaux ordonnancent des threads utilisateurs, ce qui permet plus
de flexibilité tout en profitant des SMP (exemple: bibliothèque marcel de
PM2)



  En espérant que ces précisions soient utiles,
    Vincent


Reply to: