wiki:processus_thread

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

--

Création dynamique des processus et des thread

1) 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 suppose qu’il existe un ordonnanceur par coeur, qui a pour rôle d’ordonnancer les threads qui ont été placés sur ce coeur.

1.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 FORK_RPC (bloquante), 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.

1.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 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.

2) Création d’un thread dans un cluster distant

Il y a deux 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. On s’intéresse uniquement ici aux threads “user”.

Un thread “user” peut être dans 6 états:

  • CREATED : créée mais pas encore éligible
  • RUNABLE : éligible par l’ordonnanceur
  • RUNNING : en cours d’exécution
  • BLOCKED : non éligible, en attente d’une ressource
  • COMPLETED : non éligible, en attente de destruction effective suite à un pthread_join()
  • UNUSED : le descripteur de thread peut être réutilisé pour un nouveau 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]).

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

  • TYPE : KERNEL / USER / IDLE
  • TRDID : thread identifier (contient les coordonnées [X,Y,L] du coeur)
  • PID : processus identifier du processus contenant le thread
  • STATE : CREATED / RUNNABLE / RUNNING / BLOCKED / COMPLETED
  • 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.

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.

2.1) Le noyau du cluster K envoie une THREAD_REQ_RPC au cluster Z propriétaire. Les arguments sont le PID du processus, le cluster cible M, la fonction à exécuter et ses arguments, … To Be Completed …

2.2) Le noyau du cluster Z propriétaire, envoie au cluster cible M une THREAD_CREATE_RPC dont les arguments sont le PID du processus P, la fonction à exécuter et ses arguments, … To Be Completed …

2.3) Le noyau du cluster Y vérifie s’il possède une copie du descripteur du processus P. Si ce n’est pas le cas il crée et enregistre un nouveau descripteur du processus P dans Y, et alloue les structures PT(P,X), VSL(P,X), FDT(P,X), TRDL(P,X). Il initialise les structures VSL et FDT en utilisant des remote_memcpy() depuis le cluster Z. La PT 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’ordonnancer le nouveau thread T’. Il attribue un TDID au thread à T’, crée un descripteur de thread et l’enregistre dans la TRDL. Finalement, il enregistre T’ dans l’ordonnanceur du coeur sélectionné, et renvoie le TRDID dans la réponse RPC au cluster Z.

2.4) 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) est la seule à contenir la totalité des descripteurs de threads du processus P. Puis il renvoie une réponse RPC au cluster X.

3) Destruction d’un thread

On suppose que 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.

3.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 Private. Les arguments sont le PID et le TRDID.

3.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.

3.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.

4) Destruction d’un processus

Cette section n’est qu’une ébauche… [AG]

On suppose que 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.

4.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 EXIT_REQ_RPC vers le cluster Z propriétaire.

4.2) Pour exécuter la EXIT_REQ_RPC, le noyau du cluster Z propriétaire de P broadcaste une ALL_DELETE_BCRPC vers tous les clusters M qui contiennent au moins un thread du processus P. L’argument est le PID du processus P.

4.3) Dans chaque cluster M, le noyau recevant une ALL_DELETE_RPC enregistre le signal KILL dans les descripteurs des 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. Finalement, il retourne la réponse au cluster Z.

4.4) Lorsque toutes les réponses au ALL_DELETE_BCRCP 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.