Projet système PC : 2016 - Nathanael Couret, Jean De la Fayolle, Jean-Benjamin Rousseau

De Ensiwiki
Aller à : navigation, rechercher
ChOSsure
[[Fichier:]]
Projet_système 2016, la pointure de l'OS

Développeurs COURET Nathanael
DE LA FAYOLLE Jean
ROUSSEAU Jean-Benjamin

Présentation

ChOSsure est un système d'exploitation développé dans le cadre du projet de fin de deuxième année d'apprentissage par Jean De la Fayolle, Jean-Benjamin Rousseau et Nathanaël Couret.

ChOSsure c'est vraiment la pointure de l'OS.

Cahier des charges

Les spécifications sont disponibles ici : Projet système : spécification

Outils

  • Git : dépôt Git fourni par l'Ensimag pour la gestion de version.
  • Trello : organisation du travail entre les membres de l'équipe.
  • CLion : IDE C/C++ produit par JetBrains

Planning

Planning prévisionnel

Planning prev jean nath ben.png

Planning réel

Planning reel jean nath ben.png

Réalisation

Phase 1

Cette phase inclut la gestion de l'écran à partir du code écrit en cours de logiciel de base l'année précédente. Il a principalement fallu reprendre et réorganiser le code existant et surtout traduire le code assembleur en code C. Cette phase a duré une journée et n'a pas présenté de problème majeur.

Implémentation

100 %

Tests

100 %

Phase 2

Cette phase inclut les processus et le changement de contexte. Nous avons dans un premier temps implanté un changement de contexte explicite entre 2 processus. Nous avons ensuite implanté un ordonnanceur qui effectue les changements de contexte en fonction de l'horloge.

Le mécanisme d'interruption matérielle de l'horloge effectué en TP de logiciel de base est très utile pour cette phase. Il s'agit presque d'un copier-coller, avec un transcodage en C pour certaines fonctions (cependant non indispensable). La fonction de changement de contexte en assembleur étant déjà fournie, il fallait surtout gérer les règles d'un passage de processus à l'autre (priorité et états des processus).

Implémentation

100 %

Tests

100 %

Phase 3

Cette phase s'inscrit dans la continuité de la phase précédente et introduit un cycle de vie des processus. Une liste de primitives système est à implanter et reprend les grands principes de gestion des processus : kill, waitpid, exit...

Ces implantations de primitives processus vont petit à petit en changer la structure ainsi que le fonctionnement de l'ordonnanceur pour prendre en compte les nouveaux états (attente de fils, endormissement...)

Cette phase aide à bien comprendre le fonctionnement des processus entre eux à l'aide de scenarii : un processus peut-il se suicider ? Peut-on tuer un processus zombie ? Que se passe-t-il en cas de waitpid en chaîne ? Les tests fournis dans le projet sont assez exhaustifs à ce niveau et il sera utile de relire les spécifications dans le détail.

Implémentation

100 %

Tests

100 %

Phase 4

Cette phase introduit les files de messages. Les files sont basées sur le modèle producteurs-consommateurs où des processus (les producteurs) vont mettre des données sur la file et d'autre processus (les consommateurs) vont récupérer les données mises dans la file par les producteurs. Il faut bien gérer l'endormissement des processus si la file est pleine (pour les producteurs) ou si elle est vide (pour les consommateurs).

Cette phase n'était pas très compliquée à implémenter étant donné que les spécifications expriment assez clairement le fonctionnement des primitives mais le début s'est avéré difficile car il y a de nombreux cas complexes à gérer vis-à-vis de l'endormissement des processus en attente sur une file. En outre, la validation des tests s'est avérée compliquée car nous avions mal interprété les spécifications. Il a donc fallu retravailler de grandes parties du code pour passer les tests fournis.

Implémentation

100 %

Tests

100 %

Phase 5

Nous avons décomposé cette phase en deux grandes étapes :

Bibliothèque d'appels système

Les appels systèmes codés côté noyau ne sont (heureusement) pas disponibles côté utilisateur, il faut donc les invoquer par l'interruption logicielle 49. La fonction à exécuter est identifiable par un entier (une correspondance entier/fonction système est à mettre en place) et les arguments nécessaires sont également passés lors de l'interruption. Des vérifications peuvent être faites côté utilisateur telles que la validité des adresses (on ne peut raisonnablement accéder à des adresses dans le segment du noyau depuis le mode utilisateur).

Gestion du mode utilisateur

Lancement du mode utilisateur

Cette toute première étape consiste à vérifier que l'on est capable d'atteindre le code côté utilisateur. Il a fallu créer dans le noyau un contexte côté utilisateur afin que le noyau y accède en exécutant l'instruction iret.

Il est nécessaire de bien comprendre le contenu des piles d'exécution côté noyau et utilisateur afin de les remplir convenablement et de visualiser ce que fera iret.

Retour en mode noyau depuis l'utilisateur

L'étape précédente consistant à passer de l'utilisateur au noyau, celle-ci était logiquement le passage dans le sens inverse. Le but est, à l'issue de cette étape, de pouvoir effectuer des allers-retours entre mode noyau et utilisateur.

Le mécanisme de passage du mode utilisateur au mode noyau se fait par le biais d'une interruption logicielle (différente des interruptions matérielles utilisées par exemple pour les interruptions d'horloge) appelée explicitement par le mode utilisateur. De la même manière que l'étape précédente, la compréhension du contenu des piles est essentielle.

Exit côté utilisateur

Tous les appels systèmes pouvant être correctement effectués depuis le mode utilisateur, il est tout à fait possible d'utiliser un exit explicite pour quitter un processus côté utilisateur. Cependant un problème se pose pour l'exit implicite (sur un return ou une fin de processus) : l'adresse de la fonction traitant cette fin de processus doit se trouver côté utilisateur mais le noyau doit la connaître lors de l'initialisation de la pile d'un processus. Plusieurs solutions plus ou moins satisfaisantes sont possibles, comme le mappage direct (s'inspirer de celui de user_start) ou le codage de la fonction côté noyau et sa copie dynamique côté utilisateur.

Tests

Une fois la phase 5 terminée, les tests doivent être de nouveaux exécutés côté user pour vérifier le bon fonctionnement du développement.

Implémentation

100 %

Tests

100 %

Phase 6

Cette phase correspond à l'implémentation d'une console. Dans un premier temps nous avons réalisé un pilote de clavier fonctionnant par interruption grâce aux appels système cons_read et cons_write. cons_read permet de récupérer et stocker dans un tampon les caractères tapés alors que cons_write affiche les caractères tapés à l'écran.

La principale difficulté a été de comprendre l'interaction entre la fonction keyboard_data (appelée lors d'une interruption clavier) et les appels systèmes susnommés. Cette phase nous a permis d'affiner notre compréhension des interruptions et leur lien avec l'ordonnanceur.

Implémentation

100 %

Tests

100 %

Phase 7

Durant cette phase nous avons implémenté un programme utilisateur: l'interprète de commande (shell). Il exploite la console développée précédemment pour recevoir des commandes tapées au clavier par l'utilisateur et exécuter lesdites commandes dans un nouveau processus si elles existent. Aucune difficulté majeure rencontrée si ce n'est que Nathanaël ne sait pas coder en C.

Cette phase a également été l'occasion de développer une petite démo pour la soutenance.

Implémentation

100 %

Tests

100 %