Projet système PC : 2014 - BELLINO Jullian, REMY Kevin, ROMBOURG Jérémy

De Ensiwiki
Aller à : navigation, rechercher
Ramoloss.png
Titre du projet RAMol'OS
Cadre Projet Système 2014

Équipe Kevin Rémy Jérémy Rombourg Jullian Bellino
Encadrants Grégory MOUNIE Gaëtan MORIN David BENIAMINE

Présentation du projet

Description

Ce projet intervient suite à notre cours de Systèmes d'Exploitation et Programmation Concurrente suivis tout au long de l'année.

Dans le cadre du projet système en fin de seconde année à l'ENSIMAG, nous avions comme objectif de développer un système d'exploitation. Le but principal de ce système est de proposer la gestion des processus, l'isolation kernel / user, un shell permettant l'intéraction avec l'utilisateur...etc. Toutes les phases seront détaillées un peu plus loin. Vous trouverez l'intégralité des spécifications au sein même de ce wiki.

Résultat de la commande "ramolos".

Equipe

Pour rappel l'équipe de développement est composée de :

Screen cast

Vous trouverez une vidéo de présentation de l'OS sur youtube (~4mn)

Rendu du projet

Vous pouvez à tout moment récupérer notre kernel, pour ainsi le booter sur une machine virtuelle par exemple. Nous avons également monter une clef usb afin de pouvoir démarrer sur RAMol'OS à partir d'une machine physique.

Avancement du projet

Phases obligatoires

Le projet est découpé en phases précises que nous devons suivre au fur et à mesure.

Nous avons choisi de définir le niveau de difficulté de chaque phase que nous avons estimé sur une échelle de 0 à 5. Cela pourra vous permettre d'estimer le temps nécessaire à chaque phase ainsi que votre avancement tout au long du projet.

  • Phase 1 : Prendre en main l'environnement de développement (chargement et débogage de noyau) et gérer l'affichage à l'écran. (Difficulté : 0, Durée : 1 jour.)

Cette phase est une introduction au projet. Il s'agit notamment de prendre en main l'environnement QEMU afin d'émuler une machine démarrant sur notre noyau. Le but ensuite est d'arriver à afficher des caractères à l'écran (printf), qui reste quelque chose d'essentielle. De plus il faut penser à tous les cas possibles au niveau de la gestion du curseur. Celui doit suivre tout ce qui se passe à l'écran.

  • Phase 2 : Gérer la notion de processus, et le changement de contexte entre deux processus (au niveau noyau). (Difficulté : 2, Durée : 2 jours.)

Maintenant que l'affichage est géré, la première notion de processus intervient. Le but ici est de mettre en place la création de processus simples (pid, nom, stack...). Une fois cela mis en place, nous allons essayer de passer entre deux processus. Pour cela il faut changer de contexte, c'est-à-dire charger en mémoire les informations concernant le second processus à la place du premier.

  • Phase 3: Gérer l'ordonnancement, la création dynamique, la terminaison et la filiation des processus (toujours au niveau noyau). (Difficulté : 3, Durée : 3 jours.)

La difficulté principale de cette phase réside dans l'ordonnancement des processus. En effet maintenant que nous savons comment passer d'un processus à l'autre, il va falloir réussir à ordonnancer ces changements selon certaines euristiques. Dans un premier temps il est conseillé d'effectuer un simple round-robin, puis d'introduire la notion de priorité une fois le concept maîtrisé. Une fois cela géré, il suffit d'effectuer notamment la terminaison des processus, ainsi que la filiation entre eux. Cela introduit la notion de zombie notamment, attention à bien penser à tous les cas de figures. Cette phase est essentielle pour la suite, vous devez vous assurer qu'elle soit maîtrisée.

  • Phase 4 : Gérer la communication (via l'emploi de file de message (pour les apprentis 2A: de sémaphores) ) et l'endormissement (appel wait_clock) des processus. (Difficulté : 3, Durée : 2 jours.)

La phase 4 est une phase un peu à part car celle-ci peut-être commencée avant d'avoir complètement terminée la précédente. En effet il s'agit d'implémenter la notion de sémaphores. Cette notion a été vue de nombreuses fois en cours, cela ne devrait pas être trop difficile à implémenter. De plus les tests fournis vous aideront à penser à tous les cas éventuels.

  • 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. (Difficulté : 5, Durée : 7 jours.)

Cette phase est une phase cruciale, et assez complexe à aborder. En effet jusqu'à maintenant notre système s'exécute uniquement en mode kernel. Or cela est impensable, il faut absolument séparer l'espace mémoire kernel et user pour des questions notamment de sécurité. Avant de vous lancer là-dedans il est important de bien comprendre la notion de mode kernel / user. De plus ne vous lancer pas tout de suite dans le passage au mode user, prenez le temps de comprendre les différents mécanismes. Concrètement il va falloir créer un processus "init" permettant le passage en mode user. Il n'existera donc que deux processus lancés en mode kernel : idle et init. Une fois cela effectué, il reste à réussir à revenir en mode kernel à travers des appels systèmes. Etant apprentis, nous ne sommes pas passés par la mémoire virtuelle.

AttentionPlusieurs conseils seront abordés dans la section suivante.


  • Phase 6 : Développer un pilote de console. (Difficulté : 1, Durée : 1 jour.)

Une fois la phase précédente terminée, le reste paraît presque reposant. En effet la partie console consiste principalement à gérer les interruptions clavier. Le but est d'écrire et lire ce qu'il se passe à l'écran.

  • Phase 7 : Développer un interprète de commandes, shell. (Difficulté : 1, Durée : 2 jours.)

Nous avons maintenant tout ce qu'il nous faut afin de mettre en place un shell. Ceci a déjà été réalisé en principe lors d'un TP du cours de SEPC, il est assez simple de le mettre en place de façon basique.

Extensions

Comme extension nous avons notamment choisi de mettre en place un shell "amélioré". Nous avons notamment mis en place deux buffers :

  • Le premier consiste à pouvoir parcourir les différentes commandes déjà tapées dans le shell.
  • Le second consiste à parcourir notre console afin de pouvoir accèder à d'anciens résultats de commandes par exemple. Cela peut être pratique lorsque la taille du résultat dépasse la taille de la console (25 lignes dans notre cas).

Nous avons également implémenté le multi-shell, par conséquent chaque shell aura son propre buffer de commande et son propre buffer de console.

De plus notre shell propose de nombreuses commandes :

Commande Description

help

Affiche l'ensemble des commandes disponibles ainsi que leurs descriptions.

ramolos

Invoque le tout puissant Ramoloss !

shell

Crée un nouveau processus shell.

tests

Lancement de la batterie de tests.

test [num]

Lancement d'un test précis, identifié par son numéro.

clear

Efface l'écran

kill [pid]

Tue le processus ayant comme pid le numéro passé en paramètre.

ps

Affiche la liste de tous les processus du système.

reboot

Redémarre le système.

sysinfo

Affiche les informations générales du système (processus et sémaphores).

time

Affiche l'âge de RAMol'OS !

crash

Tout est dans le nom de la commande. Simule l'appel à une instruction privilégiée.

color

Pour les moins RAMol'OS d'entre nous, passe la couleur du shell blanc sur fond noir.

shell-chg [pid_shell]

Permet d'accéder au shell qui a le pid précisé en paramètre.

shells-for-life

Permet de lancer 1000 shells en seul coup. Et ouais, 1000 ! B-)

shell-pid

Donne le pid du shell courant.

De plus plusieurs raccourcis sont à votre disposition :

Raccourci Description

Ctrl + L

Efface l'écran.

Ctrl + C

Tue le processus courant ainsi que ses fils.

Page up / Page down

Permet de parcourir l'historique de la console.

Up / Down

Permet de parcourir l'historique des commandes.

Gestion de projet

Méthodes de travail

Afin de mener à bien notre projet nous avons fait plusieurs choix au niveau de la gestion de l'équipe :

  • Nous n'avons pas utilisé de méthodes spécifiques type scrum, ou gantt. En effet la roadmap étant déjà mise en place, nous nous sommes basés là dessus afin de répartir les tâches et de juger de notre avancement.
  • Utilisation d'un gestionnaire de versions : git. Nous avons fait le choix de ne pas faire de branches pour des raisons expliquées dans le point qui suit. L'erreur que nous avons peut-être fait est de ne pas avoir publié une version pour chaque phase validée. Cela ne nous a pas posé de problèmes durant le projet mais cela aurait pu nous porter préjudice.
  • Dans les phases assez complexes du projet, nous avons préféré travailler à plusieurs sur une même tâche plutôt que d'essayer à tout prix d'avancer sur plusieurs tâches à la fois. En effet, certains développements demandent beaucoup de compréhension, nous étions simplement plus efficaces à réfléchir à plusieurs plutôt que de se lancer chacun de notre côté. De plus nous avons souvent travaillé par deux, auquel cas, nous avons essayé d'alterner la personne qui développe. Nous avons pu remarqué qu'il s'agissait d'une méthode s'avérant assez afficace.

Difficultés rencontrées et conseils

Principaux obstacles

  • Il est compliqué de travailler sur les machines de l'école (Cent'OS). En effet de nombreux problèmes d'émulations avec QEMU surviennent. Il est fortement conseillé de ne pas s'atarder sur les PC de l'école si vous avez une machine à disposition. Nous avons travaillés sur plusieurs types de système fonctionnant :
    • Debian / Ubuntu
    • Arch'Linux
    • Fedora
  • Les informations concernant le projet système sur l'ensiwiki sont éparpillées un peu partout, malgré le fait qu'on est souvent l'impression de rien trouver il faut persévérer. Beaucoup d'informations importantes s'y trouvent. De plus il ne faut surtout pas hésiter à chercher par sois-même. De nombreux sites sont très bien fournis (voir notamment les liens utiles).
  • La principale difficulté rencontrée dans ce projet système a été le passage en mode user. Pour tenter de vous apporter certaines réponses, nous allons essayer de vous décrire le comportement complet entre le mode kernel et le mode user.
    • Dans un premier temps, il faut réussir à créer un processus passant dans le mode user. Pour cela il faut positionner différents registres (DS, ES, FS, GS, SS) à 0x4B et un registre (CS) à 0x43. Vous pouvez à tout moment repasser en mode kernel en chargeant les premiers avec 0x18 et le dernier avec 0x10. Certains registres peuvent être modifiés à la main. Cependant vous devez utiliser l'instruction iret pour remplir SS, CS ainsi que les flags (0x202).
    • Suite à cela vous devriez réussir à jump dans le processus user_start préalablement défini dans user/start.c. Il vous reste plus qu'à effectuer des appels systèmes afin de repasser en mode kernel. Pour cela il suffit d'effectuer une interruption (49) tout en ayant push au préalable les arguments nécessaires à votre appel. Côté kernel l'appel système va être traité au niveau du traitant, en fonction du numéro de primitive reçu.

A partir de là vous pouvez passer en mode user tout en effectuant des appels systèmes à travers votre kernel.

  • La terminaison en mode user peut être un problème. La solution que nous avons choisi est de définir notre terminaison dans user/crt0.S. Notre terminaison effectue en fait un appel système exécutant la terminaison développée précédemment côté kernel. L'avantage de placer cet appel dans crt0.S se traduit dans le fait que l'adresse de cet appel est fixe. Cela veut dire que l'on peut empiler dans la stack du processus cette adresse. Ainsi un processus terminé effectue un appel système de terminaison. Cet appel sera également connu du kernel à la compilation grâce à l'adressage fixe.

Liens utiles

AttentionIl est très important de parcourir dans son intégralité la documentation de l'ensiwiki. Cependant les différents sites que nous vous proposons ci-dessous nous ont été très utiles, notamment osdev qui est un indispensable dans le monde de l'OS.