TP10 : Création / destruction des Processus et des Threads
1. Objectifs
Le but de ce TP est de présenter les mécanismes de création et de destruction d'un processus utilisateur.
Almos-mkh est un OS multi-kernel. Chaque cluster de l'architecture dispose d'un noyau avec ses données globales, allouées à la compilation ou créées dynamiquement, dans lesquels sont définies les structures permettant de gérer les ressources du cluster. Les structures représentant les processus et les threads sont donc nécessairement distribuées et/ou répliquées dans tous les clusters.
2. Principes Généraux
Un processus est le conteneur des ressources nécessaires à l'exécution d'une application. Parmi ces ressources, il y a l'espace d'adressage virtuel, les fichiers ouverts et l'ensemble des threads contenus dans ce processus. Un processus mono-thread est un cas particulier du cas général d'un processus multi-threads. Par conséquent, tout processus contient au moins un thread appelé main thread.
Nous avons déjà vu que l'espace d'adressage virtuel est défini par un ensemble de segments dans l'espace virtuel du processus (VSL) et d'une table de pages (GPT) qui contient le mapping des pages virtuelles dans l'espace physique. Nous avons vu également qu'il existe plusieurs types de segments en fonction de leur usage (code, piles, data, etc.) et que chaque type (privé ou public ; localisé ou distribué) suit une politique spécifique pour son mapping. Nous avons vu aussi que lorsqu'un processus s'étend sur plusieurs clusters, chaque cluster contient une partie des structures qui décrivent la mémoire du processus (VSL et GPT). Les structures sont fondamentalement distribuées (comme les segments de piles) mais certaines parties sont identiques dans tous les clusters, et donc sont plutôt répliquées (comme les segments de codes).
Un processus (père) peut créer un nouveau processus (fils) par clonage, en utilisant l'appel système fork(), qui duplique le descripteur de processus père (structure process_t) pour créer un descripteur de processus fils identique (ou presque). Le processus fils est généralement créé dans un autre cluster, pour distribuer la charge. Puis, le processus fils peut (ou pas) se transformer pour exécuter une nouvelle application, en utilisant l'appel système exec() .
Lors de la création d'un processus P, Almos-mkh crée systématiquement un thread main, représenté par un descripteur de thread (structure thread_t), dans le même cluster que le descripteur de processus P. Lorsqu'il s'exécute, ce thread main peut créer d'autres threads, dans le même cluster ou dans des clusters différents, en utilisant l'appel système pthread_create(). Ceci peut entraîner la création de copies des descripteurs du process P dans ces nouveaux clusters, si elle n'existaient pas encore.
Cette création des threads et des processus n'est déjà pas une opération triviale sur un système normal, mais elle est particulièrement délicate pour almos-mkh, en raison de la distribution/réplication des structures dans plusieurs clusters.
3. Questions
Pour répondre aux questions qui suivent, il faut commencer par lire la documentation décrivant les structures process_t et thread_t, ainsi que les opérations de création et de destruction, que vous trouverez ici.
La description des appels de commandes à distance (appelées RPC pour Remote Procedure Call) utilisés pour la création et la destruction des threads et des processus est décrite ici.
On rappelle que le typage des segments de mémoire virtuelle, et la politique de réplication/distribution, sont décrits ici.
- Comment est construit le PID identifiant un processus ? Quel est l'utilité de ce type d'encodage du PID ?
- Pour un processus P s'étalant sur plusieurs clusters, il existe un descripteur de processus dans chaque cluster contenant au moins un thread de P. Parmi toutes ces copies, on distingue le cluster owner, le cluster reference et des clusters copy. Quel est le rôle de chacun ?
- À part le cas (non implémenté actuellement) de la migration complète d'un processus mono-thread, Almos-mkh ne supporte pas la migration des threads dans le cas général ? En quoi ce choix simplifie-t-il l'ordonnancement des threads ? En quoi la migration de thread peut-elle dégrader les performances ? Dans Almos-mkh, quel problème - plus grave - interdit en pratique la migration des threads ?
- Combien y a-t-il de types de thread ? A quoi correspondent ces différents types ?
- Qu'est-ce que la VSL ? Quels sont les différents types de VSEG d'un processus utilisateur (autres que les VSEGS permettant l'accès au code et aux données du noyau lors des appels système) ? Qu'y a-t-il dans une GPT ?
- Quand un process est sur plusieurs clusters, pourquoi chaque copie de La VSL, dans chaque cluster, n'est-il pas une simple copie (éventuellement incomplète) du contenu de la VSL du cluster de référence ?
- Pourquoi a-t-on un problème de cohérence entre les différentes copies de la VSL d'un même processus dans différents clusters ?
- Dans la création d'un nouveau process, quel est le rôle de fork() et quel est le rôle d'exec() ?
- Ou se trouve le bit COW (Copy On Write) ? A quoi sert-il ? Est-il toujours utilisé ? A quel moment est-il modifié ?
- Soit un process P avec 3 threads T0, T1 et T2. T1 exécute fork() mais pas exec(). Combien aura-t-on de process et de threads par process ?
- Qu'est-ce qu'une RPC ?
- Quelle différence y a-t-il entre une RPC simple et une RPC parallèle ?
- Il existe deux modes de terminaison d'un thread, par suicide ou meurtre. Quel appel système correspond à un suicide ? Quel appel système correspond à un meurtre ? Quelle est la fonction système appelée dans ces deux cas ? Pourquoi est-il plus simple de terminer un thread s'exécutant en mode détaché ?
4. Travaux pratiques
Remarques préliminaires ::
- Pour faciliter le parcours du code, vous pouvez utiliser Visual Studio Code et ouvrir le répertoire Almos-mkh. Tous les fichiers d'Almos-mkh apparaissent alors dans la colonne de gauche. Vous pouvez par exemple ouvrir kernel/kern/process.c et ensuite parcourir le code en hyper-texte.
- L'application
idbg.elf
est lancée parload /bin/user/idbg.elf
. Cette application permet d'afficher dans la fenêtre term0 l'état de certaines structures du noyau.- La commande
h
permet d'obtenir de l'aide. - La commande
p
, affiche le nombre de processus sur le cluster 0. - La commande
s
, affiche le nombre de threads alluoués au core 0 du cluster 0.
- La commande
- Pour modifier la plateforme, vous devez éditer le fichier
almos-mkh/params-hard.mk
, par exemple pour passer de1
à2
clusters, changez l'état de la variableY_SIZE
. Puis vous devez suivre les étapes ci-après:- Recompiler Almos-mkh pour produire le disque,
- Puis recompiler le preloader (dans
tsar/softs/tsar_boot/
) pour produire la rom. - Puis recompiler le simulateur,
- Puis exécuter le simulateur avec
./simul -THREADS N
. THREADS est le nombre de threads d’OpenMP. Avec 2 clusters, on demande 2 threads d'OpenMP sur 2 cores du PC (votre PC a 6 cores et N doit être inférieur ou égal à 6).
Vous allez exécuter une application sur une plateforme à 1 puis 2 clusters, en utilisant l'instrumentation proposée par Almos-mkh pour afficher les événements concernant les processus et les threads grâce aux variables DEBUG_PROCESS_xxx
et DEBUG_THREAD_xxx
présentes dans kernel/kernel_config.h.
Rappelez-vous que quand vous modifiez le code du noyau, vous devez recompiler avec make clean ; make
pour régénérer le disque, mais vous n’êtes pas obliger de recompiler le preloader et le simulateur.
Commençons par une plateforme de 1 cluster de 1 core.
- Démarrer le simulateur et exécuter l'application
hello.elf
et l'applicationidbg.elf
. Chaque application doit être lancée dans un terminal différent. Commencez toujours par démarreridbg.elf
avanthello.elf
(à cause de la vitesse du simulateur). Les étapes de fork() et exec() des processksh
s'affichent dansterm0
, - Commentez les messages affiché sur le terminal 0 concernant le
ksh [n°1]
(vous pouvez vous aider du code des fonctions sys_fork() et sys_exec() (dans le noyau) qui se trouve ici : kernel/kern/process.h et kernel/kern/process.c). - Notez les dates de création des process et des threads.
- Afficher l'état de l'espace d'adressage du process hello.elf.
Vous allez maintenant utiliser 2 clusters de 1 cores. Vous allez suivre la création du process hello.elf grâce aux messages écrits par le noyau et en regardant le code du noyau.
- Modifiez l'état des variables
DEBUG_RPC_PROCESS_MAKE_FORK
etDEBUG_RPC_THREAD_KERNEL_CREATE
du fichierkernel/kernel_config.h
pour voir les commandes RPC. - Comme il y a 2 clusters, il y a de la réplication pendant la phase de boot (avant l'affichage de la bannière). Notez ce qui est répliqué et à quel moment.
- Avec
idbg.elf
(ou juste avec les messages du noyau) dîtes comment sont répartis les threads de l'applicationhello.elf
. - Représenter le graphe d'appel des fonctions
sys_fork()
,sys_exec()
etsys_thread_create()
. Dans ce graphe, vous ne faites pas figurer les arguments (c'est pour savoir quelles sont les fonctions appelées), vous ne mettez pas non plus le code de DEBUG. - Représenter en pseudo-code la trace d'exécution du lancement de l'application
hello.elf
.