Projet système : roadmap : Différence entre versions

De Ensiwiki
Aller à : navigation, rechercher
(Introduction)
(Phase 3)
 
(35 révisions intermédiaires par 8 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
 
{{Maintenance par enseignants}}
 
{{Maintenance par enseignants}}
 +
{{2A}} {{Informatique}}
 +
 +
[[Catégorie:Deuxième Année]]
 +
[[Catégorie:Informatique]]
 +
[[Catégorie:Projets de spécialité]]
  
 
== Résumé ==
 
== Résumé ==
Ligne 30 : Ligne 35 :
 
== Plan de développement ==
 
== Plan de développement ==
  
Le développement du projet système s'effectue en sept phases obligatoires et une
+
Le développement du projet système s'effectue en sept phases obligatoires et une partie facultative. Les premières étapes sont similaires aux séances du cours [[4MMPS|PSE]] et votre code peut être réutilisé. Néanmoins, il faut absolument en vérifier et tester la qualité. Nous vous conseillons de le reprendre étape par étape.
partie facultative. En quelques mots, les différentes étapes consistent à :
+
 
 +
 
 +
En quelques mots, les différentes étapes consistent à :
 
; [[#Phase 1|Phase 1]] : prendre en main l'environnement de développement (chargement et débogage de noyau) et gérer l'affichage à l'écran ;
 
; [[#Phase 1|Phase 1]] : prendre en main l'environnement de développement (chargement et débogage de noyau) et gérer l'affichage à l'écran ;
 
; [[#Phase 2|Phase 2]] : gérer la notion de processus, et le changement de contexte entre deux processus (au niveau noyau) ;
 
; [[#Phase 2|Phase 2]] : gérer la notion de processus, et le changement de contexte entre deux processus (au niveau noyau) ;
 
; [[#Phase 3|Phase 3]] : gérer l'ordonnancement, la création dynamique, la terminaison et la filiation des processus (toujours au niveau noyau) ;
 
; [[#Phase 3|Phase 3]] : gérer l'ordonnancement, la création dynamique, la terminaison et la filiation des processus (toujours au niveau noyau) ;
; [[#Phase 4|Phase 4]] : gérer la communication (via l'emploi de sémaphores) et l'endormissement (appel [[Projet_système_:_spécification#wait_clock - Attente temporisée|wait_clock]]) des processus ;
+
; [[#Phase 4|Phase 4]] : gérer la communication (via l'emploi de file de message) et l'endormissement (appel [[Projet_système_:_spécification#wait_clock - Attente temporisée|wait_clock]]) des processus ;
; [[#Phase 5|Phase 5]] : ajouter le mode utilisateur ;
+
; [[#Phase 5|Phase 5]] : séparer les espaces mémoires du noyau et des processus en utilisant la mémoire virtuelle (facultatif pour les apprentis) ; ajouter le mode utilisateur ;
 +
<!-- {{PC_FPGA|texte_pc=séparer les espaces mémoires du noyau et des processus en utilisant la mémoire virtuelle (facultatif pour les apprentis)&nbsp;; ajouter le mode utilisateur&nbsp;;|texte_fpga=développer le chargeur de noyau, et mettre en place une séparation des codes noyau et utilisateur, en s'inspirant du mode utilisateur (sur PC)&nbsp;;}} -->
 
; [[#Phase 6|Phase 6]] : développer un pilote de console&nbsp;;
 
; [[#Phase 6|Phase 6]] : développer un pilote de console&nbsp;;
 
; [[#Phase 7|Phase 7]] : développer un interprète de commandes, ''shell''&nbsp;;
 
; [[#Phase 7|Phase 7]] : développer un interprète de commandes, ''shell''&nbsp;;
Ligne 47 : Ligne 55 :
 
Pour bien maîtriser l'évolution de votre travail au fil du projet, nous vous
 
Pour bien maîtriser l'évolution de votre travail au fil du projet, nous vous
 
invitons à conserver une version de votre code pour au moins chacune des phases
 
invitons à conserver une version de votre code pour au moins chacune des phases
validées, en utilisant l'outil de gestion de versions svn, et nous vous demandons
+
validées, en utilisant l'outil de gestion de versions git, et nous vous demandons
 
de faire un point régulier chaque semaine avec un enseignant.
 
de faire un point régulier chaque semaine avec un enseignant.
  
 
== Phase 1 ==
 
== Phase 1 ==
  
Cette partie consiste à prendre en main l'[[Projet_système_:_environnement|environnement de travail]] qui vous sera
+
Cette partie consiste à prendre en main l'[[Projet_système_:_environnement|environnement de travail]] qui vous sera nécessaire tout au long du projet. La principale différence avec les projets précédents concerne la façon de tester et déboguer le code que vous produisez. Nous conseillons d'utiliser Qemu comme émulateur de PC pour le travail courant, et parfois Bochs (cf. l'[[Projet_système_:_environnement|environnement de travail]]).  
nécessaire tout au long du projet. La principale différence avec les projets
+
précédents concerne la façon de tester et déboguer le code que vous produisez.
+
  
Passer de l'environnement d'exécution Unix, que vous avez l'habitude d'utiliser
+
Passer de l'environnement d'exécution Unix, que vous avez l'habitude d'utiliser pour vos projets, à un PC quasiment nu (ou un simulateur de PC quasiment nu) change beaucoup de choses&nbsp;: le démarrage du programme, le support des entrées/sorties,
pour vos projets, à un PC quasiment nu change beaucoup de choses&nbsp;: le démarrage
+
du programme, le support des entrées/sorties,
+
 
le support de la <tt>libc</tt>, les facilités de débogage.
 
le support de la <tt>libc</tt>, les facilités de débogage.
  
* Ce n'est plus un ''shell'' qui lance votre programme mais un chargeur développé spécifiquement pour cette tâche. Il faut donc respecter sa convention de chargement basée sur le standard ''multiboot'' (ceci est déjà implanté dans le code de base qui vous est fourni).
+
* Sur une machine nue, ce n'est plus un ''shell'' qui lance votre programme mais un chargeur développé spécifiquement pour cette tâche. Il faut donc respecter sa convention de chargement basée sur le standard ''multiboot'' (ceci est déjà implanté dans le code de base qui vous est fourni).
 +
<!-- * {{PC_FPGA|texte_pc=Sur une machine nue, ce n'est plus un ''shell'' qui lance votre programme mais un chargeur développé spécifiquement pour cette tâche. Il faut donc respecter sa convention de chargement basée sur le standard ''multiboot'' (ceci est déjà implanté dans le code de base qui vous est fourni).|texte_fpga=Ce n'est plus un ''shell'' qui lance votre programme mais le debugger qui charge la mémoire (dans un premier temps), puis un chargeur que vous développerez en [[#Phase 5|Phase 5]].}} -->
 +
* L'absence du support usuellement fourni par le noyau Unix nous prive des E/S. L'absence de gestion des entrées n'est pas un handicap majeur pour les cinq premières phases du projet mais les sorties sont bien évidemment nécessaires dès le début pour pouvoir déboguer. La mémoire vidéo étant couplée en mémoire principale, les écritures à l'écran se font sans faire intervenir les interruptions. Ceci peut donc se faire sans problème dans cette phase. La page de la [https://ensiwiki.ensimag.fr/index.php?title=4MMPS_Ecran&oldid=58278 séance 1 de PSE] donne des détails.
 +
<!-- {{PC_FPGA|texte_pc=La mémoire vidéo étant couplée en mémoire principale, les écritures à l'écran se font sans faire intervenir les interruptions. Ceci peut donc se faire sans problème dans cette phase. La page de la [[4MMPS_Ecran|séance 1 de PSE]] donne des détails.|texte_fpga=La sortie se faisant par le port série, la visualisation des messages envoyés par le système sera visible sur un terminal série connecté au système (par exemple avec l'utilitaire <tt>cu</tt>, si on utilise la carte avec un câble série connecté au PC de développement, ou avec <tt>vpio</tt> lancé depuis <tt>xmd</tt> si on utilise la plateforme virtuelle). Au niveau de cette phase, la gestion de l'interface série, se fera sans interruption (attente active).}} -->
 +
Il est important de vérifier le bon fonctionnement de vos primitives d'affichage car c'est sur elles que reposeront vos tests tout au long du projet.
  
* L'absence du support usuellement fourni par le noyau Unix nous prive des E/S. L'absence de gestion des entrées n'est pas un handicap majeur pour les cinq premières phases du projet mais les sorties sont bien évidemment nécessaires dès le début pour pouvoir déboguer. La mémoire vidéo étant couplée en mémoire principale, les écritures à l'écran se font sans faire intervenir les interruptions. Ceci peut donc se faire sans problème dans cette phase. Il est important de vérifier le bon fonctionnement de vos primitives d'affichage car c'est sur elles que reposeront vos tests tout au long du projet.
+
* L'absence de support de la <tt>libc</tt>, nous prive notamment d'E/S formatées, de gestion de la mémoire, d'<tt>exit</tt>. Pour la gestion de la mémoire, nous vous fournissons un allocateur pour la mémoire du noyau, nécessaire dès la [[#Phase 3|Phase 3]], ainsi qu'un allocateur pour les piles utilisateur des processus, nécessaire à partir de la [[#Phase 5|Phase 5]]. Pour pallier le manque de <tt>libc</tt> en ce qui concerne les sorties, nous vous fournissons une mini bibliothèque, incluant notamment <tt>printf</tt>, à interfacer avec vos primitives de gestion de l'écran pour faciliter votre débogage. Pour la terminaison d'un programme, vous pouvez faire un appel à <tt>void reboot()</tt> qui a pour effet de redémarrer le système.
  
* L'absence de support de la <tt>libc</tt>, nous prive notamment d'E/S formatées, de gestion de la mémoire, d'<tt>exit</tt>. Pour la gestion de la mémoire, nous vous fournissons un allocateur pour la mémoire du noyau, nécessaire dès la [[#Phase 3|Phase 3]], ainsi qu'un allocateur pour les piles utilisateur des processus, nécessaire à partir de la [[#Phase 5|Phase 5]]. Pour pallier le manque de <tt>libc</tt> en ce qui concerne les sorties, nous vous fournissons une mini bibliothèque, incluant notamment <tt>printf</tt>, à interfacer avec vos primitives de gestion de l'écran pour faciliter votre débogage. Pour la terminaison d'un programme, vous pouvez faire un appel à <tt>void reboot()</tt> qui a pour effet de redémarrer la machine.
+
* Pour le débogage, vous avez toujours la possibilité d'utiliser <tt>gdb</tt>/<tt>ddd</tt> sur PC <!-- ou <tt>mb-gdb</tt> pour le FPGA -->. Il y a quelques petites différences d'utilisation liées au fait que le code à déboguer s'exécute sur une machine (virtuelle) distante mais les fonctionnalités offertes par le débogueur sont les mêmes. Par ailleurs, vous êtes privés de la mémorisation de l'interaction faite par un <tt>xterm</tt> ainsi que de la possibilité de rediriger les sorties de votre programme dans un fichier. Vous êtes donc limités à ne pouvoir inspecter à un instant donné que les 25 lignes affichables sur l'écran.
 
+
<!--{{PC_Only|texte_pc=Vous êtes donc limités à ne pouvoir inspecter à un instant donné que les 25 lignes affichables sur l'écran.}} -->
* Au niveau du débogage, vous avez toujours la possibilité d'utiliser <tt>gdb</tt>/<tt>ddd</tt>. Il y a quelques petites différences d'utilisation liées au fait que le code à déboguer s'exécute sur une machine distante mais les fonctionnalités offertes par le débogueur sont les mêmes. Par ailleurs, vous êtes privés de la mémorisation de l'interaction faite par un <tt>xterm</tt> ainsi que de la possibilité de rediriger les sorties de votre programme dans un fichier. Vous êtes donc limités à ne pouvoir inspecter à un instant donné que les 25 lignes affichables sur l'écran.
+
  
 
== Phase 2 ==
 
== Phase 2 ==
Ligne 73 : Ligne 80 :
 
C'est une phase importante car elle introduit des [[Projet_système_:_spécification#Présentation générale|concepts clés]] pour le projet.
 
C'est une phase importante car elle introduit des [[Projet_système_:_spécification#Présentation générale|concepts clés]] pour le projet.
 
Il y a peu de code à produire mais il faut prendre le temps de bien cerner et
 
Il y a peu de code à produire mais il faut prendre le temps de bien cerner et
comprendre tout ce qu'elle implique. Elle consiste à gérer&nbsp;:
+
comprendre tout ce qu'elle implique. Le cours de PSE est une bonne introduction, très guidée, sur les bases de cette phases. Les séances et documentation de cours de PSE correspondantes sont: [[4MMPS_x86|assembleur (Séance TP PSE)]], [https://ensiwiki.ensimag.fr/index.php?title=4MMPS_PIT&oldid=58277 timer (Séance TP PSE)], [https://ensiwiki.ensimag.fr/index.php?title=4MMPS_Processus&oldid=58279 processus (Séance TP PSE)], [[4MMPS_Yield|changement de contexte (Séance TP PSE)]]
 +
 
 +
 
 +
La phase consiste à étendre et généraliser ce qui est fait en PSE. En particulier, il faut gérer&nbsp;:
 +
 
 +
* dans un premier temps, la notion de [[Projet_système_:_spécification#Les processus|processus (spécification du projet)]] et de changement de contexte entre deux processus&nbsp;;
 +
* dans un second temps, le [[Projet_système_:_spécification#L'horloge|''timer''(spécification du projet)]] et les interruptions, pour obtenir un système à '''temps partagé'''.
  
* dans un premier temps, la notion de [[Projet_système_:_spécification#Les processus|processus]] et de changement de contexte entre deux processus&nbsp;;
+
'''Un paquetage gérant des listes avec priorité est fourni.'''
* dans un second temps, le [[Projet_système_:_spécification#L'horloge|''timer'']] et les interruptions, pour obtenir un système à '''temps partagé'''.
+
  
 
Jusqu'à la [[#Phase 4|Phase 4]] incluse, les processus à gérer s'exécutent uniquement en
 
Jusqu'à la [[#Phase 4|Phase 4]] incluse, les processus à gérer s'exécutent uniquement en
Ligne 122 : Ligne 134 :
 
  while (1){
 
  while (1){
 
  printf("A"); /* l'autre processus doit afficher des 'B' */
 
  printf("A"); /* l'autre processus doit afficher des 'B' */
  asm volatile ("sti"); /* demasquage des interruptions */
+
  __asm__ __volatile__ ("sti"); /* demasquage des interruptions */
 
  /* une ou plusieurs it du timer peuvent survenir pendant cette boucle d'attente */
 
  /* une ou plusieurs it du timer peuvent survenir pendant cette boucle d'attente */
 
  for (i = 0; i < 5000000; i++);  
 
  for (i = 0; i < 5000000; i++);  
  asm volatile ("cli"); /* masquage des interruptions */
+
  __asm__ __volatile__ ("cli"); /* masquage des interruptions */
 
  }
 
  }
 
  }
 
  }
Ligne 133 : Ligne 145 :
 
Cette étape est la suite directe de la phase précédente. Elle consiste à gérer
 
Cette étape est la suite directe de la phase précédente. Elle consiste à gérer
 
tout ce qui concerne le [[Projet_système_:_spécification#Vie d'un processus|''cycle de vie'']] des processus, toujours en mode
 
tout ce qui concerne le [[Projet_système_:_spécification#Vie d'un processus|''cycle de vie'']] des processus, toujours en mode
superviseur. Nous vous conseillons de procéder par étapes, dans l'ordre qui
+
superviseur. Les séances et documentations de PSE qui correspondent à la base du projet, sont: [https://ensiwiki.ensimag.fr/index.php?title=4MMPS_Sched&oldid=32238 ordonnancement (Séance TP PSE)], [https://ensiwiki.ensimag.fr/index.php?title=4MMPS_Sleep&oldid=58305 endormissement (Séance TP PSE)], [https://ensiwiki.ensimag.fr/index.php?title=4MMPS_Exit&oldid=58306 terminaison (Séance TP PSE)].
 +
 
 +
Nous vous conseillons de procéder par étapes, dans l'ordre qui
 
suit&nbsp;:
 
suit&nbsp;:
 
* gestion de l'[[Projet_système_:_spécification#Ordonnancement - Scheduling|ordonnancement]] par le ''scheduler'', et de la création dynamique des processus&nbsp;;
 
* gestion de l'[[Projet_système_:_spécification#Ordonnancement - Scheduling|ordonnancement]] par le ''scheduler'', et de la création dynamique des processus&nbsp;;
Ligne 143 : Ligne 157 :
 
== Phase 4 ==
 
== Phase 4 ==
  
Cette phase consiste à implémenter les [[Projet_système_:_spécification#Les files de messages|files de messages]] et le cas échéant la
+
Cette phase consiste à implémenter les [[Projet_système_:_spécification#Les files de messages|files de messages]] et le cas échéant la gestion de l'endormissement de processus.
gestion de l'endormissement de processus.
+
 
Les tests minimaux à valider à la fin de cette phase sont&nbsp;:
 
Les tests minimaux à valider à la fin de cette phase sont&nbsp;:
  
; [[Projet_système_:_spécification#Les files de messages|files de messages]] : un système producteur/consommateur(s).
+
; [[Projet_système_:_spécification#Les files de messages|Les files de messages]]: implantation d'un anneau à jeton.
 +
<!-- ; [[Projet_système_:_spécification#Les sémaphores (en option)|Les sémaphores (en option)]]: implantation du problème des philosophes. -->
 
; [[Projet_système_:_spécification#wait_clock - Attente temporisée|wait_clock]]
 
; [[Projet_système_:_spécification#wait_clock - Attente temporisée|wait_clock]]
 
: le programme principal lance 4 processus P1 à P4 et exécute un <tt>wait_clock</tt> d'une minute&nbsp;;
 
: le programme principal lance 4 processus P1 à P4 et exécute un <tt>wait_clock</tt> d'une minute&nbsp;;
Ligne 165 : Ligne 179 :
  
 
C'est une phase délicate. L'ensemble des problèmes qu'elle pose doit être pensé
 
C'est une phase délicate. L'ensemble des problèmes qu'elle pose doit être pensé
comme un tout.
+
comme un tout. Vous devez penser à implémenter de manière incrémentales les différentes étapes présentées ci-dessous. L'ordre conseillé (et imposé si vous réalisez la mémoire virtuelle) est documenté ci-après, chacune des étapes se doit d'être soigneusement testée pour éviter l'accumulation de problèmes compliqués à débugguer.
 +
# '''Protection mémoire :''' Séparer la mémoire du noyau et de l'application (en utilisant la mémoire virtuelle si votre projet le requiert)
 +
# '''Appels système :''' Créer une bibliothèque d'appels vers le noyau, elle devient obligatoire dès qu'un processus est isolé du noyau.
 +
## Fournir à l'espace utilisateur la possibilité d'appeler le noyau (à l'aide d'interruptions logicielles)
 +
## Permettre au noyau de recevoir les interruptions des applications pour traiter leur requêtes
 +
# '''Protection d'éxecution (du code) :''' utiliser les niveaux de privilège de votre processeur pour isoler l'éxecution d'une application utilisateur et du noyau dans deux contextes différents. L'interruption provoquée par le monde utilisateur provoque un changement de niveau de protection
 +
 
 +
Ces différentes étapes sont documentées de manière plus détaillée ci-après. Pour les précisions techniques, il convient de se reporter aux pages suivantes:
 +
*[[Projet système : Aspects techniques]]
 +
*[[Projet système : mémoire virtuelle]]
  
 
=== Tour des problèmes ===
 
=== Tour des problèmes ===
Ligne 173 : Ligne 196 :
 
Dans les phases précédentes, les programmes de tests étaient inclus  
 
Dans les phases précédentes, les programmes de tests étaient inclus  
 
directement dans le noyau, sous forme de fonctions. L'application en mode  
 
directement dans le noyau, sous forme de fonctions. L'application en mode  
utilisateur, elle, n'est pas liée avec le noyau. Elle est liée séparément et  
+
utilisateur, elle, n'est pas liée avec le noyau. Elle est liée séparément  
chargée à l'adresse 16&nbsp;M. Il faut donc bien avoir conscience que le noyau et  
+
et chargée à l'adresse 16&nbsp;M ou à l'adresse virtuelle 1&nbsp;G si vous implémentez la mémoire virtuelle.
 +
<!-- {{PC_FPGA|texte_pc=et chargée à l'adresse 16&nbsp;M ou à l'adresse virtuelle 1&nbsp;G si vous implémentez la mémoire virtuelle.|texte_fpga=et chargée à l'adresse 0x2018 0000}} -->
 +
Il faut donc bien avoir conscience que le noyau et  
 
l'application sont deux programmes indépendants qui ne partagent pas de  
 
l'application sont deux programmes indépendants qui ne partagent pas de  
mémoire. Le <tt>crt0</tt> du noyau a mis en place une protection qui empêche  
+
mémoire.
l'application de lire ou écrire des données du noyau. Pour la même raison, il  
+
Le <tt>crt0</tt> du noyau a mis en place une protection qui empêche l'application de lire ou écrire des données du noyau. Pour la même raison, il est impossible pour l'application d'appeler des fonctions du noyau.
est impossible pour l'application d'appeler des fonctions du noyau.
+
<!--{{PC_FPGA|texte_pc=Le <tt>crt0</tt> du noyau a mis en place une protection qui empêche l'application de lire ou écrire des données du noyau. Pour la même raison, il est impossible pour l'application d'appeler des fonctions du noyau.|texte_fpga=Si le processeur le permettait il serait possible de mettre en place une protection qui empêche l'application de lire ou écrire des données du noyau dans le <tt>crt0</tt> du noyau. Pour cette raison (et se placer dans ce contexte), on s'interdira d'appeler directement des fonctions du noyau depuis l'application.}} -->
  
 
==== Réalisation d'une bibliothèque des appels noyau ====
 
==== Réalisation d'une bibliothèque des appels noyau ====
Ligne 184 : Ligne 209 :
 
Dans les étapes précédentes, l'application appelait les services du
 
Dans les étapes précédentes, l'application appelait les services du
 
noyau par pur appel procédural. Dès que le noyau et l'application sont séparés,
 
noyau par pur appel procédural. Dès que le noyau et l'application sont séparés,
ceci n'est plus possible. En outre, il faut réaliser une transition du mode esclave vers
+
ceci n'est plus possible.
le mode maître. L'application ne peut le faire qu'en exécutant une instruction
+
En outre, il faut réaliser une transition du mode esclave vers le mode maître. L'application ne peut le faire qu'en exécutant une instruction <tt>int</tt>.
<tt>int</tt>. Comme on veut laisser au programmeur la facilité d'écrire dans son
+
<!--{{PC_FPGA|texte_pc=En outre, il faut réaliser une transition du mode esclave vers le mode maître. L'application ne peut le faire qu'en exécutant une instruction <tt>int</tt>.|texte_fpga=En outre, on réalisera un appel simulant la transition d'un mode esclave (interruptible) vers un mode maître (non interruptible). L'application le fera en exécutant un appel à une routine placée dans <tt>USER VECTOR</tt> (cf. documentation du Microblaze p.31).}} -->
 +
Comme on veut laisser au programmeur la facilité d'écrire dans son
 
source des appels au noyau par appel de fonction, il faut écrire tout un
 
source des appels au noyau par appel de fonction, il faut écrire tout un
 
ensemble de fonctions (une par appel noyau) qui auront pour but d'exécuter une
 
ensemble de fonctions (une par appel noyau) qui auront pour but d'exécuter une
Ligne 195 : Ligne 221 :
 
==== Écriture du module de récupération de l'<tt>int</tt> ====
 
==== Écriture du module de récupération de l'<tt>int</tt> ====
  
Puisque l'application déclenche une interruption par <tt>int</tt>,
+
Puisque l'application déclenche une interruption par <tt>int</tt>, il faut bien la récupérer dans le noyau.
il faut bien la récupérer dans le noyau.
+
<!-- {{PC_FPGA|texte_pc=Puisque l'application déclenche une interruption par <tt>int</tt>, il faut bien la récupérer dans le noyau.|texte_fpga=Puisque l'application déclenche un appel noyau, il faut bien le récupérer dans le noyau.}} -->
 
Il est donc nécessaire d'écrire un traitant pour gérer ces interruptions
 
Il est donc nécessaire d'écrire un traitant pour gérer ces interruptions
 
logicielles. Les remarques évoquées au sujet du traitant des interruptions
 
logicielles. Les remarques évoquées au sujet du traitant des interruptions
 
''timer'' en [[#Phase 2|Phase 2]], quant au découpage assembleur / C, restent valables ici.
 
''timer'' en [[#Phase 2|Phase 2]], quant au découpage assembleur / C, restent valables ici.
 +
<!-- {{FPGA_Only|texte_fpga=Il faudra mettre en place le masquage des interruptions non prévu dans le cas de l'appel à <tt>USER VECTOR</tt>.}} -->
 
Le point délicat ajouté dans leur gestion
 
Le point délicat ajouté dans leur gestion
 
est de retrouver les paramètres de l'appel noyau pour les passer
 
est de retrouver les paramètres de l'appel noyau pour les passer
à la routine du noyau qui en a besoin. Il y a deux stratégies possibles pour
+
à la routine du noyau qui en a besoin.
transmettre les paramètres&nbsp;:
+
Il y a deux stratégies possibles pour transmettre les paramètres&nbsp;:
 
* les stocker sur la pile utilisateur&nbsp;;
 
* les stocker sur la pile utilisateur&nbsp;;
 
* utiliser les registres du processeur.
 
* utiliser les registres du processeur.
 
+
La première solution est assez compliquée, en particulier parce qu'elle oblige à gérer d'éventuels défauts de page. Il est beaucoup plus simple et élégant de recourir à la seconde solution. Cela limite le nombre de paramètres à six, mais ce n'est pas un problème. Nous vous demandons d'utiliser la solution basée sur les registres.
La première solution est assez compliquée, en particulier parce qu'elle oblige à
+
<!-- {{PC_Only|texte_pc=Il y a deux stratégies possibles pour transmettre les paramètres&nbsp;:
gérer d'éventuels défauts de page. Il est beaucoup plus simple et élégant de
+
* les stocker sur la pile utilisateur&nbsp;;
recourir à la seconde solution. Cela limite le nombre de paramètres à six, mais
+
* utiliser les registres du processeur.
ce n'est pas un problème. Nous vous demandons d'utiliser la solution basée sur
+
La première solution est assez compliquée, en particulier parce qu'elle oblige à gérer d'éventuels défauts de page. Il est beaucoup plus simple et élégant de recourir à la seconde solution. Cela limite le nombre de paramètres à six, mais ce n'est pas un problème. Nous vous demandons d'utiliser la solution basée sur les registres.}} -->
les registres.
+
<!--{{FPGA_Only|texte_fpga=La stratégie à adopter pour transmettre les paramètres est la suivante&nbsp;:
 +
* utiliser les registres du processeur.}} -->
  
 
==== Lancement des processus de l'application en mode esclave ====
 
==== Lancement des processus de l'application en mode esclave ====
Ligne 218 : Ligne 246 :
 
l'application par un appel procédural. Ceci n'est plus possible lorsque le noyau
 
l'application par un appel procédural. Ceci n'est plus possible lorsque le noyau
 
s'exécute en mode maître et l'application en mode esclave puisque le lancement
 
s'exécute en mode maître et l'application en mode esclave puisque le lancement
d'un processus par le noyau correspond à une transition de mode. Il faut donc
+
d'un processus par le noyau correspond à une transition de mode.
trouver un moyen de lancer un processus en faisant une transition de mode.
+
Il faut donc trouver un moyen de lancer un processus en faisant une transition de mode. Il faut pour cela utiliser une instruction <tt>iret</tt>.
Il faut pour cela utiliser une instruction <tt>iret</tt>.
+
<!-- {{PC_FPGA|texte_pc=Il faut donc trouver un moyen de lancer un processus en faisant une transition de mode. Il faut pour cela utiliser une instruction <tt>iret</tt>.|texte_fpga=Pour faire comme si le processeur était capable de gérer les transitions de mode, il faut donc trouver un moyen de lancer un processus en faisant une transition de mode. Il faut pour cela utiliser une instruction <tt>retid</tt>.}}-->
 
Une application doit s'exécuter avec les interruptions démasquées.
 
Une application doit s'exécuter avec les interruptions démasquées.
  
 
==== Gestion de deux piles par processus ====
 
==== Gestion de deux piles par processus ====
  
Lorsque le processeur <tt>x86</tt> réalise une transition du mode esclave vers le
+
Lorsque le processeur <tt>x86</tt> réalise une transition du mode esclave vers le mode maître, il procède également à un changement de pile. Lors de la transition esclave vers maître, le pointeur de pile est chargé avec une valeur prise dans la structure <tt>TSS</tt>, et lors de la transition maître vers esclave, le registre <tt>esp</tt> est chargé avec la valeur sauvegardée dans la pile maître. Ce mécanisme est imposé par le processeur, il est impossible d'y échapper. À partir de cette phase, il faut donc gérer deux piles par processus, une pour le mode maître et une pour le mode esclave.
mode maître, il procède également à un changement de pile. Lors de la transition
+
<!-- {{PC_FPGA|texte_pc=Lorsque le processeur <tt>x86</tt> réalise une transition du mode esclave vers le mode maître, il procède également à un changement de pile. Lors de la transition esclave vers maître, le pointeur de pile est chargé avec une valeur prise dans la structure <tt>TSS</tt>, et lors de la transition maître vers esclave, le registre <tt>esp</tt> est chargé avec la valeur sauvegardée dans la pile maître. Ce mécanisme est imposé par le processeur, il est impossible d'y échapper. À partir de cette phase, il faut donc gérer deux piles par processus, une pour le mode maître et une pour le mode esclave.|texte_fpga=Le principe des 2 piles est à mettre en place pour l'implémentation sur le microblaze.}} -->
esclave vers maître, le pointeur de pile est chargé avec une valeur prise dans
+
la structure <tt>TSS</tt>, et lors de la transition maître vers esclave, le
+
registre <tt>esp</tt> est chargé avec la valeur sauvegardée dans la pile maître. Ce
+
mécanisme est imposé par le processeur, il est impossible d'y échapper. À partir
+
de cette phase, il faut donc gérer deux piles par processus, une pour le mode
+
maître et une pour le mode esclave.
+
  
 
==== Protection de l'espace des entrées/sorties ====
 
==== Protection de l'espace des entrées/sorties ====
  
L'espace des registres d'E/S ne doit être accessible qu'en mode maître. Cette
+
L'espace des registres d'E/S ne doit être accessible qu'en mode maître. Cette protection peut être mise en oeuvre en affectant un <tt>iopl</tt> de 0 dans les ''flags'' de l'application. Lorsque cette protection est mise en place, l'application ne peut donc pas écrire directement. Pour les besoins des sorties des programmes de test, vous devrez donc créer un appel système [[Projet_système_:_spécification#cons_write - Affichage sur le terminal|cons_write]] pour gérer l'affichage à l'écran. Le formatage des sorties effectué par <tt>printf</tt> doit quant à lui rester en mode utilisateur.
protection peut être mise en oeuvre en affectant un <tt>iopl</tt> de 0 dans les
+
<!-- {{PC_Only|texte_pc=L'espace des registres d'E/S ne doit être accessible qu'en mode maître. Cette protection peut être mise en oeuvre en affectant un <tt>iopl</tt> de 0 dans les ''flags'' de l'application. Lorsque cette protection est mise en place, l'application ne peut donc pas écrire directement. Pour les besoins des sorties des programmes de test, vous devrez donc créer un appel système [[Projet_système_:_spécification#cons_write - Affichage sur le terminal|cons_write]] pour gérer l'affichage à l'écran. Le formatage des sorties effectué par <tt>printf</tt> doit quant à lui rester en mode utilisateur.}} -->
''flags'' de l'application. Lorsque cette protection est mise en place,
+
 
l'application ne peut donc pas écrire directement. Pour les besoins des sorties
+
=== Chargeur du noyau ===
des programmes de test, vous devrez donc créer un appel système
+
 
[[Projet_système_:_spécification#cons_write - Affichage sur le terminal|cons_write]]
+
Le chargement du noyau est réalisable de nombreuse façons: par grub2, par l'émulateur (option <tt>-kernel</tt> de qemu), boot cdrom/hdd sur une image disque ou bien encore pxe (boot réseaux). Il n'y a rien à développer par rapport au squelette fourni.
pour gérer l'affichage à l'écran. Le formatage des sorties
+
 
effectué par <tt>printf</tt> doit quant à lui rester en mode utilisateur.
+
<!-- {{FPGA_Only|texte_fpga=À partir de cette phase, on demande de mettre en place un chargeur du noyau. Ce programme sera mis  dans la bram (0x0000 à 0x1FFF). Au ''reset'' du système, la carte envoie sur le port série un message du type : "Attente du noyau". À partir de ce moment, les octets du noyau pourront être envoyés à la carte (via le port série) afin d'être placés aux adresses 0x2010 0000 et suivantes. Un fois le noyau chargé en mémoire, le système démarre.}} -->
  
 
=== Tests ===
 
=== Tests ===
Ligne 257 : Ligne 279 :
 
* terminer
 
* terminer
  
À l'exécution, on doit voir le premier message mais pas le second car lors de
+
À l'exécution, on doit voir le premier message mais pas le second car lors de l'exécution de l'instruction privilégiée, il doit se produire une exception, que le noyau pourra récupérer pour tuer le programme fautif. Cette gestion des exceptions n'est pas obligatoire dans le cahier de charges de base du projet, et si elle n'est pas implémentée, votre noyau plantera en cas d'exception.
l'exécution de l'instruction privilégiée, il doit se produire une exception, que
+
<!-- {{PC_Only|texte_pc=Écrire et faire fonctionner le programme suivant&nbsp;:
le noyau pourra récupérer pour tuer le programme fautif. Cette
+
* imprimer le message <tt>Je démarre</tt>
gestion des exceptions n'est pas obligatoire dans le cahier de charges de base
+
* exécuter une instruction privilégiée
du projet, et si elle n'est pas implémentée, votre noyau plantera en cas
+
* imprimer le message <tt>Je stoppe</tt>
d'exception.
+
* terminer
 +
 
 +
À l'exécution, on doit voir le premier message mais pas le second car lors de l'exécution de l'instruction privilégiée, il doit se produire une exception, que le noyau pourra récupérer pour tuer le programme fautif. Cette gestion des exceptions n'est pas obligatoire dans le cahier de charges de base du projet, et si elle n'est pas implémentée, votre noyau plantera en cas d'exception.}} -->
  
 
==== Test de la protection de la mémoire et de l'espace d'E/S ====
 
==== Test de la protection de la mémoire et de l'espace d'E/S ====
  
C'est une variation du programme précédent&nbsp;: au lieu d'exécuter une instruction
+
C'est une variation du programme précédent&nbsp;: au lieu d'exécuter une instruction privilégiée, le programme essaye d'accéder à l'espace d'E/S, par exemple en tentant de modifier la position du curseur, ou à une zone mémoire protégée, par exemple la mémoire vidéo. Là aussi, le noyau pourra récupérer l'exception et tuer le programme fautif.
privilégiée, le programme essaye d'accéder à l'espace d'E/S, par exemple en
+
<!-- {{PC_Only|texte_pc=C'est une variation du programme précédent&nbsp;: au lieu d'exécuter une instruction privilégiée, le programme essaye d'accéder à l'espace d'E/S, par exemple en tentant de modifier la position du curseur, ou à une zone mémoire protégée, par exemple la mémoire vidéo. Là aussi, le noyau pourra récupérer l'exception et tuer le programme fautif.}} -->
tentant de modifier la position du curseur, ou à une zone mémoire protégée, par
+
exemple la mémoire vidéo. Là aussi, le noyau pourra récupérer l'exception et
+
tuer le programme fautif.
+
  
 
==== Test du passage de paramètres ====
 
==== Test du passage de paramètres ====
  
Écrire un programme qui crée plusieurs processus en leur passant des paramètres
+
Écrire un programme qui crée plusieurs processus en leur passant des paramètres différents. Les processus se contentent d'imprimer les paramètres reçus et se terminent.
différents. Les processus se contentent d'imprimer les paramètres reçus et se
+
<!-- {{PC_Only|texte_pc=Écrire un programme qui crée plusieurs processus en leur passant des paramètres différents. Les processus se contentent d'imprimer les paramètres reçus et se terminent.}} -->
terminent.
+
  
 
== Phase 6 ==
 
== Phase 6 ==
  
Cette partie consiste à implémenter une [[Projet_système_:_spécification#Le pilote de console|console]]. Les sorties sur écran étant
+
Cette partie consiste à implémenter une [[Projet_système_:_spécification#Le pilote de console|console]].
normalement opérationnelles depuis la [[#Phase 1|Phase 1]], il reste à coder un [[Projet_système_:_spécification#Le clavier|pilote de clavier]]. Il faut réaliser un vrai pilote, c'est-à-dire fonctionnant par
+
 
interruptions.
+
Les sorties sur écran étant normalement opérationnelles depuis la [[#Phase 1|Phase 1]], il reste à coder un [[Projet_système_:_spécification#Le clavier|pilote de clavier]]. Il faut réaliser un vrai pilote, c'est-à-dire fonctionnant par interruptions. Ce qui vous est principalement demandé ici, c'est de gérer les événements liés au clavier et d'implémenter les appels système [[Projet_système_:_spécification#cons_read - Lecture sur le terminal|cons_read]] et [[Projet_système_:_spécification#cons_write - Affichage sur le terminal|cons_write]]. Tout ce qui concerne la reconnaissance des ''scan codes'' vous est fourni.
 +
<!-- {{PC_FPGA|texte_pc=Les sorties sur écran étant normalement opérationnelles depuis la [[#Phase 1|Phase 1]], il reste à coder un [[Projet_système_:_spécification#Le clavier|pilote de clavier]]. Il faut réaliser un vrai pilote, c'est-à-dire fonctionnant par interruptions. Ce qui vous est principalement demandé ici, c'est de gérer les événements liés au clavier et d'implémenter les appels système [[Projet_système_:_spécification#cons_read - Lecture sur le terminal|cons_read]] et [[Projet_système_:_spécification#cons_write - Affichage sur le terminal|cons_write]]. Tout ce qui concerne la reconnaissance des ''scan codes'' vous est fourni.|texte_fpga=Il faut coder un pilote d'entrée (entrée Rx du port série). Il faut réaliser un vrai pilote, c'est-à-dire fonctionnant par interruptions. Ce qui vous est principalement demandé ici, c'est de gérer les événements liés aux caractères arrivant sur le port série et d'implémenter les appels système [[Projet_système_:_spécification#cons_read - Lecture sur le terminal|cons_read]] et [[Projet_système_:_spécification#cons_write - Affichage sur le terminal|cons_write]].}} -->
  
Ce qui vous est principalement
+
Le travail qui vous est demandé concerne les mécanismes de base de gestion des E/S.
demandé ici, c'est de gérer les événements liés au clavier et d'implémenter les
+
appels système [[Projet_système_:_spécification#cons_read - Lecture sur le terminal|cons_read]] et [[Projet_système_:_spécification#cons_write - Affichage sur le terminal|cons_write]]. Tout ce qui concerne la
+
reconnaissance des ''scan codes'' vous est fourni. Le travail qui vous est demandé
+
concerne les mécanismes de base de gestion des E/S.
+
  
 
== Phase 7 ==
 
== Phase 7 ==

Version actuelle en date du 8 juin 2020 à 10:27

AttentionCette page est maintenue par les enseignants et utilisée par les élèves de la matière concernée. Vos contributions sont les bienvenues, mais merci d'en discuter avant de faire des modifications non triviales de la page, pour être sûr de ne pas perturber le déroulement du cours.

Mycomputer.png  Deuxième Année  CDROM.png  Informatique 

Résumé

Ce document est complémentaire aux spécifications du mini système d'exploitation que vous devrez programmer. Il vise à vous aider en vous fournissant une méthode de développement bien précise.

Introduction

Habituellement, la conduite d'un projet consiste à passer par les phases d'analyse du problème, de conception de l'architecture, de codage et de débogage. Cette méthode ne peut être strictement appliquée dans le cadre du projet système à cause du trop court laps de temps imparti. En effet, si on vous demandait de la respecter, il est probable que vous ne seriez en mesure de produire les premières lignes de code que très tard dans le calendrier, et qu'à la fin du projet, très peu de choses fonctionneraient. Or, nous considérons que le principal apport du projet système réside dans l'expérience que vous apportera le fait de faire fonctionner votre noyau.

Nous vous demandons donc de suivre un plan de développement en sept phases, de bien respecter dans chaque étape le cycle analyse-architecture-codage-débogage, mais d'accepter de faire chaque phase sans avoir une vision très détaillée du fonctionnement final de votre noyau. Par exemple, les phases 2 à 5 visent à vous guider pas à pas dans l'implémentation d'un noyau de processus, en raffinant progressivement le cahier des charges. Cette méthodologie ne doit pas être perçue comme une contrainte, son unique but est de vous aider à organiser le développement de votre projet.

Plan de développement

Le développement du projet système s'effectue en sept phases obligatoires et une partie facultative. Les premières étapes sont similaires aux séances du cours PSE et votre code peut être réutilisé. Néanmoins, il faut absolument en vérifier et tester la qualité. Nous vous conseillons de le reprendre étape par étape.


En quelques mots, les différentes étapes consistent à :

Phase 1 
prendre en main l'environnement de développement (chargement et débogage de noyau) et gérer l'affichage à l'écran ;
Phase 2 
gérer la notion de processus, et le changement de contexte entre deux processus (au niveau noyau) ;
Phase 3 
gérer l'ordonnancement, la création dynamique, la terminaison et la filiation des processus (toujours au niveau noyau) ;
Phase 4 
gérer la communication (via l'emploi de file de message) et l'endormissement (appel wait_clock) des processus ;
Phase 5 
séparer les espaces mémoires du noyau et des processus en utilisant la mémoire virtuelle (facultatif pour les apprentis) ; ajouter le mode utilisateur ;
Phase 6 
développer un pilote de console ;
Phase 7 
développer un interprète de commandes, shell ;
Phase d'extension 
ajout de nouvelles fonctionnalités (par exemple, espaces virtuels d'adressage).

Toutes les phases ne sont pas équivalentes en termes de difficulté et de temps de développement/mise au point. En particulier, les phases 2 et 5 concernent des concepts clés du projet et sont délicates à déboguer.

Pour bien maîtriser l'évolution de votre travail au fil du projet, nous vous invitons à conserver une version de votre code pour au moins chacune des phases validées, en utilisant l'outil de gestion de versions git, et nous vous demandons de faire un point régulier chaque semaine avec un enseignant.

Phase 1

Cette partie consiste à prendre en main l'environnement de travail qui vous sera nécessaire tout au long du projet. La principale différence avec les projets précédents concerne la façon de tester et déboguer le code que vous produisez. Nous conseillons d'utiliser Qemu comme émulateur de PC pour le travail courant, et parfois Bochs (cf. l'environnement de travail).

Passer de l'environnement d'exécution Unix, que vous avez l'habitude d'utiliser pour vos projets, à un PC quasiment nu (ou un simulateur de PC quasiment nu) change beaucoup de choses : le démarrage du programme, le support des entrées/sorties, le support de la libc, les facilités de débogage.

  • Sur une machine nue, ce n'est plus un shell qui lance votre programme mais un chargeur développé spécifiquement pour cette tâche. Il faut donc respecter sa convention de chargement basée sur le standard multiboot (ceci est déjà implanté dans le code de base qui vous est fourni).
  • L'absence du support usuellement fourni par le noyau Unix nous prive des E/S. L'absence de gestion des entrées n'est pas un handicap majeur pour les cinq premières phases du projet mais les sorties sont bien évidemment nécessaires dès le début pour pouvoir déboguer. La mémoire vidéo étant couplée en mémoire principale, les écritures à l'écran se font sans faire intervenir les interruptions. Ceci peut donc se faire sans problème dans cette phase. La page de la séance 1 de PSE donne des détails.

Il est important de vérifier le bon fonctionnement de vos primitives d'affichage car c'est sur elles que reposeront vos tests tout au long du projet.

  • L'absence de support de la libc, nous prive notamment d'E/S formatées, de gestion de la mémoire, d'exit. Pour la gestion de la mémoire, nous vous fournissons un allocateur pour la mémoire du noyau, nécessaire dès la Phase 3, ainsi qu'un allocateur pour les piles utilisateur des processus, nécessaire à partir de la Phase 5. Pour pallier le manque de libc en ce qui concerne les sorties, nous vous fournissons une mini bibliothèque, incluant notamment printf, à interfacer avec vos primitives de gestion de l'écran pour faciliter votre débogage. Pour la terminaison d'un programme, vous pouvez faire un appel à void reboot() qui a pour effet de redémarrer le système.
  • Pour le débogage, vous avez toujours la possibilité d'utiliser gdb/ddd sur PC . Il y a quelques petites différences d'utilisation liées au fait que le code à déboguer s'exécute sur une machine (virtuelle) distante mais les fonctionnalités offertes par le débogueur sont les mêmes. Par ailleurs, vous êtes privés de la mémorisation de l'interaction faite par un xterm ainsi que de la possibilité de rediriger les sorties de votre programme dans un fichier. Vous êtes donc limités à ne pouvoir inspecter à un instant donné que les 25 lignes affichables sur l'écran.

Phase 2

C'est une phase importante car elle introduit des concepts clés pour le projet. Il y a peu de code à produire mais il faut prendre le temps de bien cerner et comprendre tout ce qu'elle implique. Le cours de PSE est une bonne introduction, très guidée, sur les bases de cette phases. Les séances et documentation de cours de PSE correspondantes sont: assembleur (Séance TP PSE), timer (Séance TP PSE), processus (Séance TP PSE), changement de contexte (Séance TP PSE)


La phase consiste à étendre et généraliser ce qui est fait en PSE. En particulier, il faut gérer :

Un paquetage gérant des listes avec priorité est fourni.

Jusqu'à la Phase 4 incluse, les processus à gérer s'exécutent uniquement en mode superviseur. Ils ont une pile chacun pour leur exécution. Nous vous conseillons de commencer avec deux processus infinis, créés statiquement, qui se passent la main à tour de rôle, par un appel direct à context_switch. Les piles peuvent être allouées statiquement ou par appel à l'allocateur de mémoire. Le code suivant propose un exemple minimaliste de processus de test :

int tstA(void *arg)
{
	unsigned long i;
	while (1) {
		printf("A"); /* l'autre processus doit afficher des 'B' */
		/* boucle d'attente pour ne pas afficher trop de caractères */
		for (i = 0; i < 5000000; i++); 
		context_switch();		
	}
}

Une fois le mécanisme de base opérationnel, vous pouvez ajouter la gestion des interruptions et partager le temps d'exécution entre deux processus. Pour gérer le temps partagé, vous devez programmer le timer, ce qui ne présente aucune difficulté majeure, et gérer les interruptions matérielles qu'il génère (cf. Gestion du temps). Pour répondre aux interruptions du timer, il est nécessaire d'écrire un traitant en assembleur qui se termine par une instruction iret. Ce programme en assembleur ne doit faire que le strict nécessaire pour déléguer le gros du travail à une fonction écrite en C appelée par une instruction call. Dans un premier temps cette fonction fera toujours un changement de contexte entre les deux processus. Dans un deuxième temps, il faudra suivre le compte du temps écoulé afin de changer de contexte à une fréquence SCHEDFREQ (=50) différente de CLOCKFREQ (=100). Le code suivant propose un exemple de processus test pour le temps partagé. Le démasquage des interruptions après l'appel à printf est nécessaire pour que le processus courant puisse être interrompu. En principe, à cette exception près, tout votre code noyau doit toujours tourner en mode masqué.

int tstA(void *arg)
{
	unsigned long i;
	while (1){
		printf("A"); /* l'autre processus doit afficher des 'B' */
		__asm__ __volatile__ ("sti"); /* demasquage des interruptions */
		/* une ou plusieurs it du timer peuvent survenir pendant cette boucle d'attente */
		for (i = 0; i < 5000000; i++); 
		__asm__ __volatile__ ("cli"); /* masquage des interruptions */	
	}
}

Phase 3

Cette étape est la suite directe de la phase précédente. Elle consiste à gérer tout ce qui concerne le cycle de vie des processus, toujours en mode superviseur. Les séances et documentations de PSE qui correspondent à la base du projet, sont: ordonnancement (Séance TP PSE), endormissement (Séance TP PSE), terminaison (Séance TP PSE).

Nous vous conseillons de procéder par étapes, dans l'ordre qui suit :

  • gestion de l'ordonnancement par le scheduler, et de la création dynamique des processus ;
  • gestion de la terminaison des processus ;
  • gestion de la filiation.

Vérifiez bien que vos fonctions ont un comportement conforme à la sémantique de l'énoncé.

Phase 4

Cette phase consiste à implémenter les files de messages et le cas échéant la gestion de l'endormissement de processus. Les tests minimaux à valider à la fin de cette phase sont :

Les files de messages
implantation d'un anneau à jeton.
wait_clock
le programme principal lance 4 processus P1 à P4 et exécute un wait_clock d'une minute ;
  • le processus P1 imprime le caractère . à raison de un par seconde ;
  • le processus P2 imprime le caractère - toutes les 2 secondes ;
  • le processus P3 imprime le caractère + toutes les 5 secondes ;
  • le processus P4 imprime le caractère * toutes les 10 secondes ;
  • la périodicité est gérée par un appel à wait_clock dans tous les cas ;
  • à son réveil, le processus principal tue ses fils et se termine.
On doit voir une alternance correcte des différents caractères.

Il est important de bien déboguer cette partie, en envisageant le maximum de cas de figure. Bien penser que ces fonctionnalités de synchronisation seront utilisées dans de nombreux tests jusqu'à la fin du projet.

Phase 5

C'est une phase délicate. L'ensemble des problèmes qu'elle pose doit être pensé comme un tout. Vous devez penser à implémenter de manière incrémentales les différentes étapes présentées ci-dessous. L'ordre conseillé (et imposé si vous réalisez la mémoire virtuelle) est documenté ci-après, chacune des étapes se doit d'être soigneusement testée pour éviter l'accumulation de problèmes compliqués à débugguer.

  1. Protection mémoire : Séparer la mémoire du noyau et de l'application (en utilisant la mémoire virtuelle si votre projet le requiert)
  2. Appels système : Créer une bibliothèque d'appels vers le noyau, elle devient obligatoire dès qu'un processus est isolé du noyau.
    1. Fournir à l'espace utilisateur la possibilité d'appeler le noyau (à l'aide d'interruptions logicielles)
    2. Permettre au noyau de recevoir les interruptions des applications pour traiter leur requêtes
  3. Protection d'éxecution (du code) : utiliser les niveaux de privilège de votre processeur pour isoler l'éxecution d'une application utilisateur et du noyau dans deux contextes différents. L'interruption provoquée par le monde utilisateur provoque un changement de niveau de protection

Ces différentes étapes sont documentées de manière plus détaillée ci-après. Pour les précisions techniques, il convient de se reporter aux pages suivantes:

Tour des problèmes

Séparation physique du noyau et de l'application

Dans les phases précédentes, les programmes de tests étaient inclus directement dans le noyau, sous forme de fonctions. L'application en mode utilisateur, elle, n'est pas liée avec le noyau. Elle est liée séparément et chargée à l'adresse 16 M ou à l'adresse virtuelle 1 G si vous implémentez la mémoire virtuelle. Il faut donc bien avoir conscience que le noyau et l'application sont deux programmes indépendants qui ne partagent pas de mémoire. Le crt0 du noyau a mis en place une protection qui empêche l'application de lire ou écrire des données du noyau. Pour la même raison, il est impossible pour l'application d'appeler des fonctions du noyau.

Réalisation d'une bibliothèque des appels noyau

Dans les étapes précédentes, l'application appelait les services du noyau par pur appel procédural. Dès que le noyau et l'application sont séparés, ceci n'est plus possible. En outre, il faut réaliser une transition du mode esclave vers le mode maître. L'application ne peut le faire qu'en exécutant une instruction int. Comme on veut laisser au programmeur la facilité d'écrire dans son source des appels au noyau par appel de fonction, il faut écrire tout un ensemble de fonctions (une par appel noyau) qui auront pour but d'exécuter une instruction int. Ces fonctions seront écrites en assembleur et mises dans une bibliothèque qui jouera donc pour vos applications le rôle que joue la libc dans le système Unix.

Écriture du module de récupération de l'int

Puisque l'application déclenche une interruption par int, il faut bien la récupérer dans le noyau. Il est donc nécessaire d'écrire un traitant pour gérer ces interruptions logicielles. Les remarques évoquées au sujet du traitant des interruptions timer en Phase 2, quant au découpage assembleur / C, restent valables ici. Le point délicat ajouté dans leur gestion est de retrouver les paramètres de l'appel noyau pour les passer à la routine du noyau qui en a besoin. Il y a deux stratégies possibles pour transmettre les paramètres :

  • les stocker sur la pile utilisateur ;
  • utiliser les registres du processeur.

La première solution est assez compliquée, en particulier parce qu'elle oblige à gérer d'éventuels défauts de page. Il est beaucoup plus simple et élégant de recourir à la seconde solution. Cela limite le nombre de paramètres à six, mais ce n'est pas un problème. Nous vous demandons d'utiliser la solution basée sur les registres.

Lancement des processus de l'application en mode esclave

Dans les phases précédentes, le noyau pouvait lancer un processus de l'application par un appel procédural. Ceci n'est plus possible lorsque le noyau s'exécute en mode maître et l'application en mode esclave puisque le lancement d'un processus par le noyau correspond à une transition de mode. Il faut donc trouver un moyen de lancer un processus en faisant une transition de mode. Il faut pour cela utiliser une instruction iret. Une application doit s'exécuter avec les interruptions démasquées.

Gestion de deux piles par processus

Lorsque le processeur x86 réalise une transition du mode esclave vers le mode maître, il procède également à un changement de pile. Lors de la transition esclave vers maître, le pointeur de pile est chargé avec une valeur prise dans la structure TSS, et lors de la transition maître vers esclave, le registre esp est chargé avec la valeur sauvegardée dans la pile maître. Ce mécanisme est imposé par le processeur, il est impossible d'y échapper. À partir de cette phase, il faut donc gérer deux piles par processus, une pour le mode maître et une pour le mode esclave.

Protection de l'espace des entrées/sorties

L'espace des registres d'E/S ne doit être accessible qu'en mode maître. Cette protection peut être mise en oeuvre en affectant un iopl de 0 dans les flags de l'application. Lorsque cette protection est mise en place, l'application ne peut donc pas écrire directement. Pour les besoins des sorties des programmes de test, vous devrez donc créer un appel système cons_write pour gérer l'affichage à l'écran. Le formatage des sorties effectué par printf doit quant à lui rester en mode utilisateur.

Chargeur du noyau

Le chargement du noyau est réalisable de nombreuse façons: par grub2, par l'émulateur (option -kernel de qemu), boot cdrom/hdd sur une image disque ou bien encore pxe (boot réseaux). Il n'y a rien à développer par rapport au squelette fourni.


Tests

En fin de Phase 5, vous devez au minimum valider les tests suivants :

Test du mode utilisateur

Écrire et faire fonctionner le programme suivant :

  • imprimer le message Je démarre
  • exécuter une instruction privilégiée
  • imprimer le message Je stoppe
  • terminer

À l'exécution, on doit voir le premier message mais pas le second car lors de l'exécution de l'instruction privilégiée, il doit se produire une exception, que le noyau pourra récupérer pour tuer le programme fautif. Cette gestion des exceptions n'est pas obligatoire dans le cahier de charges de base du projet, et si elle n'est pas implémentée, votre noyau plantera en cas d'exception.

Test de la protection de la mémoire et de l'espace d'E/S

C'est une variation du programme précédent : au lieu d'exécuter une instruction privilégiée, le programme essaye d'accéder à l'espace d'E/S, par exemple en tentant de modifier la position du curseur, ou à une zone mémoire protégée, par exemple la mémoire vidéo. Là aussi, le noyau pourra récupérer l'exception et tuer le programme fautif.

Test du passage de paramètres

Écrire un programme qui crée plusieurs processus en leur passant des paramètres différents. Les processus se contentent d'imprimer les paramètres reçus et se terminent.

Phase 6

Cette partie consiste à implémenter une console.

Les sorties sur écran étant normalement opérationnelles depuis la Phase 1, il reste à coder un pilote de clavier. Il faut réaliser un vrai pilote, c'est-à-dire fonctionnant par interruptions. Ce qui vous est principalement demandé ici, c'est de gérer les événements liés au clavier et d'implémenter les appels système cons_read et cons_write. Tout ce qui concerne la reconnaissance des scan codes vous est fourni.

Le travail qui vous est demandé concerne les mécanismes de base de gestion des E/S.

Phase 7

Cette partie consiste à implémenter un interprète de commandes, qui vous sera utile pour tester et démontrer le bon fonctionnement de votre système. Il est important de comprendre que ce shell doit être un programme utilisateur. Il ne doit pas accéder aux structures de données du noyau, la protection mémoire mise en place par le crt0 qui vous est fourni l'en empêchera. Si votre shell doit récupérer des informations auprès du noyau, par exemple la liste des processus existants pour la commande ps, cela doit se faire par le biais d'appels systèmes. Vous pouvez définir tous les appels systèmes qui vous semblent nécessaires.

Phase d'extension

Avant de vous lancer dans cette phase, nous vous demandons de faire le point avec un enseignant pour :

  • vérifier l'état courant de votre implémentation ;
  • valider vos objectifs, en fonction du temps restant et de vos intérêts.

Des idées de réalisations sont proposées à la fin de la spécification du noyau.

Remerciements

Certains passages de ce document sont inspirés d'un texte de Bernard Cassagne.