wiki:processus_thread

Version 4 (modified by alain, 6 years ago) (diff)

--

Création dynamique des processus et des thread

1) descripteur de processus

Le PID (Process Identifier) est codé sur 32 bits, et il est unique dans le système : les 16 bits de poids fort contiennent les coordonnées (X,Y) du cluster propriétaire Z, les 16 bits de poids faibles (LPID) contiennent le numéro local dans le cluster Z. Le descripteur d’un processus P et les tables qui lui sont associées ne sont répliqués - partiellement- que dans les clusters qui contiennent au moins un thread de P (appelés clusters actifs de P).

Le descripteur de processus est défini par la structure task_t, et il contient les informations suivantes:

  • PID : processus identifier (contient les coordonnées du cluster propriétaire)
  • PPID : parent processus identifier,
  • XMIN, XMAX, YMIN, YMAX : recrangle recouvrant tous les clusters actifs
  • PT : table des pages du processus,
  • VSL : liste des vsegs du processus,
  • FDT : table des fichiers ouverts du processus,
  • TRDL : liste des threads du processus,
  • ENV : variables d’environnement du processus,

2) descripteur de thread

Il existe quatre types de threads :

  • un thread USER est créé suite à un appel système pthread_create().
  • un thread KERNEL est créé pour exécuter un service système.
  • un thread IDLE est exécuté par un CPU qui ne possède aucun autre thread exécutable.
  • un thread RPC est créé et ou activé par le noyau pour exécuter une ou plusieurs RPCs.

QUESTION pourquoi distingue-t-on les thread KERNEL et les thread RPC ? [AG]

Un thread peut être dans 6 états décrits dans la section [wiki: portant sur l'ordonnancement des thread.

L’OS attribue à un thread d’un processus P un identifiant unique dans le processus. Ce TRDID est codé sur 32 bits, et il est construit à partir des coordonnées [X,Y,L] du coeur assigné au thread, et de l’index LTID du thread dans l’ordonnanceur du coeur assigné au thread (par exemple X = TRDID[31:24] / Y = TRDID[25:16] / L = TRDID[15:8] / LTID = TRDID[7:0]).

ATTENTION : ce TRDID n'est pas actuellement implémentés dans ALMOS-MK. [AG]

Les principales informations stockées dans le descripteur de thread sont les suivantes :

  • TYPE : KERNEL / USER / IDLE / RPC
  • TRDID : thread identifier (contient les coordonnées [X,Y,L] du coeur)
  • PID : processus identifier du processus contenant le thread
  • STATE : CREATE / READY / USER / KERNEL / WAIT / DEAD
  • SAVE : zone de sauvegarde des registres du coeur.
  • PARENT : TRDID du thread parent qui doit être informé de la terminaison.
  • IO : canaux alloués au thread dans le cas des périphériques multi-canaux.
  • SIGNALS : vecteur de bits permettant d’enregistrer les signaux reçus par le thread
  • etc.

3) création d’un processus dans un cluster distant

Ce mécanisme a été implémenté dans ALMOS-MK par Pierre-Yves Péneau, il est décrit dans la thèse de Mohamed Karaoui. La création d’un processus distant utilise le mécanisme fork / exec. On rappelle qu’il existe un ordonnanceur par coeur, qui a pour rôle d’ordonnancer les threads qui ont été placés sur ce coeur.

3.1) fork()

Le processus père P s’exécute sur un coeur du cluster Z. Il fait un appel système fork() qui a principalement pour rôle de sélectionner un cluster cible X qui deviendra le cluster propriétaire du processus fils F, et de dupliquer le processus P dans le cluster Z. Le choix du cluster cible devrait en principe s’appuyer sur la DQDT, bien que celle-ci ne soit pas implémentée actuellement dans ALMOS-MK.

L’appel system fork() crée dans le cluster Z un nouveau descripteur de processus pour le processus clone P’, ainsi qu’un descripteur de thread pour le thread principal attaché au processus P’. Le descripteur du processus P’ contient en particulier la VSL(P’,Z), la PT(P’,Z), la FDT(P’,Z) qui sont des copies des tables correspondantes du processus P. En revanche la TRDL(P’,Z) ne contient que le thread nouvellement créé, ce qui signifie que le processus P’ clone est mono-thread. Puis l’appel système fork() demande au cluster cible X l’allocation d’un PID au moyen d’une TASK_FORK_RPC, en transmettant en argument l’adresse étendue du descripteur de processus P’. Cette adresse est enregistrée dans la table des processus du cluster, et le PID est enregistré dans le descripteur du processus P’ du cluster Z. Finalement, le fork() enregistre le nouveau thread dans l’ordonnanceur du coeur du cluster Z qui exécute le fork(), et rend la main au processus P.

3.2) exec()

Une fois l'opération fork() terminée, le processus fils P’ peut exécuter l'appel système exec(). Cet appel système exec() effectue une TASK_EXEC_RPC vers le cluster X, avec les arguments suivants: le PID du processus fils, le nom de la fonction à exécuter, les arguments de la fonction s'il y en a, les variables d'environnement. Cette RPC alloue un descripteur de processus pour le processus F, ainsi qu’un descripteur de thread. Il initialise ces descripteurs et les tables PT(F,X) , VSL(F,X), TRDL(F,X) en utilisant les arguments de la RPC. Il recopie le contenu de la FDT(P’,Z) dans la FDT(F,X), en utilisant un remote_memcpy(), puisqu’il dispose de l’adresse étendue de P’. Quand le processus P’ sur le cluster Z reçoit une réponse positive pour cette EXEC_RPC, le processus P’ intermédiaire se suicide.

Une fois que les structures sont initialisées, le thread principal du processus fils est attaché à l'ordonnanceur du cœur cible. Le code binaire (segments code et data) sera chargé dans la mémoire du cluster cible, lors du traitement des défauts de page.

4) Création d’un thread user dans un cluster distant

N’importe quel thread T de n’importe quel processus P s’exécutant sur n’importe quel cluster K peut créer un nouveau thread T’ dans n’importe quel cluster M. Ce thread T’ sera attaché à l’ordonnanceur de l’un des coeurs du cluster M. Le choix de ce coeur est effectué par l’instance du noyau du cluster M. Le cluster propriétaire du processus P peut être un troisième cluster Z.

Seul le noyau du cluster propriétaire du processus P peut créer ou détruire un thread appartenant au processus P (i.e. modifier la Liste des threads TRDL appartenant au processus P) Ceci simplifie les problèmes de concurrence, et permet de simplifier les copies des descripteurs de processus dans les clusters autres que le propriétaire de P. La copie de la liste des threads TRDL(P,M) dans un cluster M autre que le cluster propriétaire de P ne contient que les descripteurs de thread qui s’exécutent localement sur un coeur de M, alors que la TRDL(P,Z) du cluster Z propriétaire de P doit contenir tous les threads de P.

4.1) phase 1

Le noyau du cluster K envoie une THREAD_CREATE_RPC au cluster Z propriétaire. Les arguments sont le PID du processus, le cluster cible M, la fonction à exécuter et ses arguments. Le noyau du cluster Z propriétaire de P, ne répond pas immédiatement au cluster demandeur K, mais envoie au cluster cible M une THREAD_CREATE_RPC, avec les mêmes arguments.

4.2) phase 2

Le noyau du cluster M vérifie s’il possède une copie du descripteur du processus P. Si ce n’est pas le cas il crée et un nouveau descripteur du processus P dans M, et alloue les structures PT(P,M), VSL(P,M), FDT(P,M), TRDL(P,M). Il initialise les structures VSL et FDT en utilisant des remote_memcpy() depuis le cluster Z vers le cluster M. La structure PT(P,M) reste vide, et se remplira à la demande lors du traitement des défauts de page. Quand il est sûr de posséder une copie du descripteur de processus P, il sélectionne un coeur charger d’exécuter le nouveau thread T’, Il attribue un TRDID au thread à T’, crée un descripteur de thread et l’enregistre dans la TRDL(P,M). Finalement, il enregistre T’ dans l’ordonnanceur du coeur sélectionné, et renvoie le TRDID dans la réponse RPC au cluster Z.

4.3) Phase 3

Le noyau du cluster Z propriétaire de P alloue un descripteur de thread et l’enregistre dans sa TRDL(P,Z). Cette TRDL(P,Z) doit en effet contenir la totalité des descripteurs de threads du processus P. Puis le noyau de Z renvoie une réponse RPC au cluster K.

5) Destruction d’un thread

La destruction d’un thread TRDID s’exécutant sur un cluster K est déclenchée :

  • soit par le thread lui-même (appel système pthread_exit() ),
  • soit par le noyau du cluster hôte K où s’exécute le thread,
  • soit par un signal provenant d’un autre cluster.

Dans tous les cas, c’est le noyau du cluster hôte K, qui pilote cette destruction.

5.1) phase 1

Le noyau du cluster K envoie une THREAD_EXIT_RPC au cluster Z propriétaire du processus P, pour qu’il mette à jour la TRDL(P,Z) qui contient tous les thread de P. Les arguments sont le PID et le TRDID.

5.2) phase 2

Le noyau du cluster Z propriétaire de P met à jour sa TRDL(P,Z), en supprimant le thread TRDID, puis envoie la réponse RPC au cluster K.

5.3) phase 3

Le noyau du cluster K enregistre le signal KILL dans le descripteur de thread TRDID au moyen d’une opération atomique.

Lors du traitement de ce signal, le thread TRDID est éliminé de l’ordonnanceur, il est éliminé de la TRDL(P,X), et la mémoire allouée pour le descripteur de thread dans le cluster K est libérée.

5) Destruction d’un processus

La destruction d’un processus P est déclenchée :

  • soit par un thread du processus P s’exécutant sur un cluster X quelconque (appel système exit() ),
  • soit par le noyau du cluster Z propriétaire P,
  • soit par un signal provenant d’un autre cluster

Dans tous les cas, c’est le noyau du cluster Z propriétaire de P, qui pilote cette destruction.

5.1) phase 1

Si l’appel système exit() est exécuté sur un cluster K différent du cluster Z propriétaire, le noyau du cluster K envoie une TASK_EXIT_RPC vers le cluster Z propriétaire. L’argument est le PID du processus P.

5.2) phase 2

Pour exécuter la TASK_EXIT_RPC, le noyau du cluster Z propriétaire de P broadcaste une TASK_EXIT_RPC vers tous les clusters M qui contiennent au moins un thread du processus P. Dans chaque cluster M, le noyau recevant une TASK_EXIT_RPC enregistre le signal KILL dans les descripteurs detous les threadsde P. Quand il détecte que la TRDL(P,M) est vide, il libère toutes les structures de données allouées au processus P dans le cluster M, et retourne la réponse au cluster Z.

5.3) phase 3

Lorsque toutes les réponses au TASK_EXIT_RCP ont été reçues par le cluster Z, celui-ci libère toutes les structures de données allouées au processus P dans le cluster Z.