Projet système PC : 2015 - LORIER Nicolas, TURIN Loic

De Ensiwiki
Aller à : navigation, rechercher
bolOS
texte écran d'accueil de notre bolOS
Projet Système 2015

Développeurs LORIER Nicolas
TURIN Loïc

Présentation

Objectifs

L’objectif de ce projet est de réaliser un noyau de système d’exploitation sur une architecture Intel. Le noyau de notre système doit comporter les fonctionnalités suivantes :

  • Gestion des processus
  • Primitives de synchronisation entre processus
  • Temps partagé
  • Pilote d’E/S
  • Interpréteur de commande

Au delà de ces fonctionnalités de base, des extensions seront possibles comme une gestion d’un affichage graphique, un système de fichiers, etc.

Équipe

Nous étions deux à faire ce projet :

LORIER Nicolas (filière SLE)

TURIN Loïc (filière SLE)

Encadrants

Gaëtan Harter et Grégory Mounié

Gestion d'équipe

Au début du projet les enseignants scheme nous demandent de mettre en place une méthode de gestion de projet, cela a pour but de nous aider dans l'organisation de l'équipe. En tant que binôme, les problématiques d’organisation sont assez simples et généralement vites résolues. Cependant le partage des taches n'est pas facile dans ce projet, en effet, chaque phase a besoin de la précédente pour fonctionner. Nous avons mis en place un planning prévisionnel pour nous aider dans la planification des taches.

Planning prévisionnel

Planning previsionnel(BolOS).png

La phase d'extension est optionnelle. nous avions prévu d'avoir fini la phase 7 une semaine avant la fin du projet, ce qui nous laissait à peu près une semaine pour faire une extension, suivant la durée que prendrait la validation globale.

Planning effectif

Planning effectif(BolOS).png

Évidement le déroulement du projet ne s'est pas passé comme prévu, nous n'avons pas pu aller jusqu'au bout et nous nous sommes arrêter à la phase 6. Nous avons notamment pris du retard dans la phase 3 et 5. Un tel retard peut être expliqué par le fait que nous avions sous-estimé la phase 3 et 5. De plus nous avons été confronté à plusieurs bugs qui nous ont parfois jusqu'à trois jours à réparer.

Risques

Ce genre de bug constitue définitivement le plus gros risque de ce projet car ils sont souvent difficile à comprendre. Il faut exécuter le programme pas à pas, vérifier l'état des registres. Lors de l’exécution d'un programme dans la partie user, gdb ne dispose pas des symboles des sources, on est donc obligé d’exécuter les instructions asm une à une, ce qui rend le débug parfois pénible.

La documentation sur l'ensiwiki est très fournie, malheureusement très éparpillée et peut être pas très bien organisée, ce qui rend difficile toute tentative d'avoir une vision globale du projet. Lors de la phase 5 notamment, la quantité de documentation à lire et à assimiler est conséquente. Il est alors important de ne pas s'éparpiller et d'y aller petit à petit.

Réalisation

Phase 1

Cette première phase consiste à gérer l'affichage à l'écran. Elle reprend une partie du module de pratique du système.

En résumé, l'affichage fait 80 colonnes sur 25 lignes et s'effectue en écrivant directement dans la mémoire vidéo pour y placer les caractères. C'est la fonction console_putbytes appelée par printf qu'il faut implémenter.

Étant donné la réalisation préalable de l'affichage en PSE, cette phase reste extrêmement simple.

Phase 2

Cette phase consiste à gérer la notion de processus, et le changement de contexte entre deux processus ainsi qu'une horloge et les interruptions, pour obtenir un système à temps partagé.

Dans cette phase, il faut faire tout ce qui concerne la création de processus et la sauvegarde et restauration des contextes. La gestion de l'horloge matérielle implique la gestion d'interruptions, et toutes les informations nécessaires pour cette phase peuvent se trouver une fois de plus dans les pages sur les pages wiki Processus et PIT du module pratique du système.

Les processus sont répertoriés dans un tableau de taille NB_PROC : on a un nombre limité prédéfini de processus. Ils peuvent être dans un des états suivants :

  • ACTIF : c'est alors le processus en cours d'exectution
  • ACTIVABLE : le processus est dans une file de priorité que l'ordonnanceur défile pour choisir le prochain processus à élire
  • ATTENTE : il y a différent types d'attente : attente sur fils, sur entrée/sortie ou sur file de message (phases 3, 6 et 4 respectivement)
  • ENDORMI : le processus attend un certain nombre de ticks d'horloge avant d'être reveillé (phase 3)
  • ZOMBIE : le processus s'est terminé mais dépend toujours d'un père (phase 3)
  • MORT : le processus s'est terminé

De même, puisque une grande partie de cette phase a été réalisée en pratique du système, reprendre code n'a pas posé de problèmes particuliers.

Phase 3

Cette phase consiste à gérer les cycles de vie des processus : ordonnancement, endormissement, terminaison, ainsi que la filiation.

On implémente ici :

  • ordonnancement: round-robin par priorité (de 1 à 256)
  • endormissement: basé sur les ticks d'horloge materielle par appel à la fonction wait_pid
  • terminaison : par terminaison noramale (arrivée au bout du code à executer), en étant tué par un autre processus par appel explicite, ou par suicide.

C'est à partir de cette phase que l'on va plus loin que ce qui a été vu en pratique du système. Il faut par exemple dans cette phase implémenter tout ce qui concerne l'attente de processus sur leurs fils, la gestion du passage de valeurs de retour au processus père ou encore la perte de parent. La partie potentiellement délicate de cette étape est de bien respecter la spécification du projet pour gérer tous les cas possibles et pouvoir passer les tests correctement.

Nous avons mis plus de temps que prévu pour cette étape car nous avons sous-estimé l'amplitude et le temps de débug.

Phase 4

Cette phase consiste à implémenter les files de message pour la communication entre processus.

Les processus peuvent demander d'envoyer ou recevoir des informations dans des files de messages accessibles par tous les processus.

L'implémentation en elle-même des fonctions de la spécification du projet n'a pas posé de problèmes particuliers, il a simplement fallu faire attention à la manière de passer directement les messages lorsque un processus envoie un message sur une file vide sur laquelle des processus sont bloqués en attente de réception par exemple.

A ce point dans le projet, nous avons mis en place une base de test pour toutes ces fonctionnalités coté kernel en copiant les tests fournis dans le dossier user/tests/. Nous voulions assurer le bon fonctionnement de nos implémentations et nous avons pris le temps de faire passer à notre système les tests 0 à 18 du dossier de tests. Cela nous a pris un temps non-négligeable mais nous avons pu valider le fonctionnement du système jusque là.

Phase 5

Cette phase consiste à implémenter la séparation kernel/user et la mémoire virtuelle.

C'est une étape importante du projet puisque la mémoire virtuelle permet d'isoler les différents processus et qu'un processus n'accède pas à des espaces mémoires protégés. On utilise ici une pagination qui permet au processus d'utiliser des adresses comprises entre 1Go et 4Go. Nous avons pris comme convention de mapper le code du processus à 1Go, pile user du processus à 4Go et la pile kernel à 3Go. Il revient donc au système d'exploitation d'allouer la mémoire physique disponible à chaque processus.

Puisque le processus est maintenant en mode user, il n'a plus accès aux fonctions kernel. Nous avons donc mis en place les appels systèmes pour permettre à chaque processus d'utiliser les fonctionnalités implémentés dans les phases précédentes. On procède ici à des interruptions logicielles pour passer en mode kernel, exécuter l'appel demandé, puis revenir au mode user. Les processus ont donc à disposition une librairie d'appels systèmes.

Nous avons rencontré plusieurs difficultés lors de cette phase. Il nous a fallu beaucoup de temps pour prendre en main la pagination et le passage entre les modes user et kernel. Il est fortement recommandé de lire assidument la documentation fournie, notamment les slides sur la transition de mode et OSDev.

Phase 6

Cette phase consiste à implémenter un pilote pour le clavier ainsi que l'appel systeme cons_read. Le pilote de clavier n'est pas très dur à implémenter, il consiste simplement en un traitant de l'interruption 33 qui envoie le scan code à do_scan code. Ensuite il suffit de récupérer les caractères tapés au clavier et de le mettre dans une file de message (ie le tampon). Les processus appelant cons_read seront alors automatiquement mis en attente sur cette file si elle est vide et récupéreront les messages si des caractères ont été tapés.


Difficultés rencontrées

Prise en main de l'environnement de travail

Les phases de débug ont été nombreuses et longues au cours de ce projet. Une bonne maitrise de gdb est un atout de taille qui nous manquait en début de projet. La découverte des commandes suivantes a été très utile :

  • layout asm : affiche le code assembleur
  • layout regs : affiche les registres
  • si ou ni pour exécuter une instruction asm

Il faut également faire attention lors des instructions "int $xx" pour générer des interruptions matérielles : il est possible que si la prochaine instruction, c'est à dire celle du traitant de l'interruption, génère par exemple une page fault, gdb n'indique pas qu'il soit passé par cette ligne et rentrera directement dans le trap code. On a donc l'impression que c'est cette instruction int qui génère l'erreur alors qu'elle vient du traitant.

ATTENTION gcc version 4.9.2

Dans la phase 5 nous avons rencontré un bug inattendu, lorsqu'on compilait notre projet avec gcc 4.9.2 sur notre machine personnelle, celui-ci générait une instruction int 3 dans le code de la partie user, ce qui correspond en temps normal à un breakpoint du debugger. Cela avait pour effet de générer une exception trap 3. La solution a été d'installé la version 4.8 et de modifier le CC flag dans les makefile user et kernel pour utiliser cette version plutôt que celle installé par défaut. Pour connaître la version de gcc dont vous disposez, vous pouvez faire gcc --version

Bilan

Le projet système est un projet assez difficile techniquement, il nécessite une bonne prise en min de l'environnement de travail git et gdb. De plus les notions mises en œuvres sont complexes et dépassent le cadre de SEPC, CSE et PS. Encore une fois nous pensons que l'aspect le plus difficile est le débug, et ce n'est pas un aspect négligeable puisqu'une grande partie du temps de travail lui est consacré (en tout cas dans notre cas). Les phases de débug ne sont certainement pas les plus plaisantes mais sont nécessaires et formatrices.

Cependant nous avons aimé faire ce projet et nous ne regrettons absolument pas notre choix, malgré l'état incomplet de notre avancement. Il nous a beaucoup appris techniquement, et nous a permis de mettre en pratique les notions abordées dans nos cours de CSE, SEPC et PS appliquées à l'architecture intel IA32. Nous pouvons maintenant dire que maîtrisons la programmation bas-niveau sur architecture x86.

Bibliographie