Projet système PC : 2019 - AJDOR Othmane, Klein Louis

De Ensiwiki
Révision de 25 juin 2019 à 12:27 par Kleinlou (discussion | contributions) (Phase 6 : Gestion du clavier et implémentation d'un pilote de console)

Aller à : navigation, rechercher
Doot.gif
Titre du projet doot
Cadre Projet système

Équipe Othmane AJDOR, Louis Klein
Encadrants Yves Denneulin , Gregory Mounie, Patrick Reignier


Présentation

Equipe

Planning

Planning prévisionnel

Planning effectif

Phases de développement

Phase 1 : Prise en main de l'environnement

100 %

La prise en main de l’environnent a pu être un peu fastidieuse. Nous avons vraiment monté en compétence tout le long du projet, notamment dans la prise en main d'outil comme gdb, et objdump.

Phase 2 : Création et lancement de processus de niveau noyau

100 %

Tout est fonctionnel, les premiers tests sont passés avec quelques difficultés, souvent des manquements à la spécification.

Phase 3 : Ordonnancement, création dynamique et terminaison de processus de niveau noyau

100 %

Nous avons fait quelques choix de conception ici, notamment de n'avoir qu'une queue de processus, tout dans l'état activable. Les processus dans les autres états ne sont pas dans cette file d'attente. Il faut donc gérer les divers état dans les primitives kill, chrpio, waiptid etc. L'ordonnanceur ne s'occupe que de prendre le prochain processus activable, le rend actif, et effectue le context switch.

Phase 4 : Gestion des communications et synchronisation de processus de niveau noyau

100 %

Quelques difficultés dans cette partie, notamment pour bien comprendre la spec ! Lorsqu'un producteur écrit, alors que des consommateurs attendent, le message ne doit pas passer dans la file de message, il doit être immédiatement récupéré par le plus vieux consommateur. De même, lorsqu'un consommateur vient, il doit consommer le message des producteurs en attente, et non pas le plus vieux message dans la file. Ces subtilités rendent le code un peu plus compliqué à implémenter.

Phase 5 : Séparation des espaces mémoire noyau et utilisateur : gestion de processus utilisateur

100 %

C'est probablement la phase la plus subtile. Il faut bien comprendre comment les deux piles kernels et user sont gérés par le processeur, et ce qu'effectue exactement l'instruction iret.

Phase 6 : Gestion du clavier et implémentation d'un pilote de console

100 %

Phase 7 : Implémentation d'un interprète de commandes

100 %

Journal de bord

Phase 1

05/06/2019 - Mise en place de l'environnement

  • Prise en main de code de base et de l'environnement de travail
  • Mise en place des primitives d'affichage

Phase 2

06/06/2019 - Gestion de processus

  • Mise en place de changement de contexte inter-processus
  • Implementation des fonctions d'execution des processus

07/06/2019 - Gestion d'horloge et des interruptions

  • Mise en place de des fonctions de gestion d'horloge
  • Implementation des fonctions de traitement d'interruptions
  • Gestion du partage du temps CPU entre les processus


Phase 3

07/06/2019 - Ordonnancement

  • Mise en place d'un ordonnanceur classique
  • Amélioration de l'ordonnanceur en utilisant une queue FIFO
  • Endormissement des processus


11/06/2019 - Ordonnancement 2

  • Correction de quelques bugs d'endormissement
  • Allocation dynamique des processus
  • Ajout d'une fonction intermédiaire pour intercepter les valeurs de retours des processus


12/06/2019 - Ordonnancement 3

  • Améliorations mineurs de l'ordonnancement
  • Gestion des processus tués, primitives exit et kill
  • Gestion des valeurs de retours des processus
  • Mise en place de waitpid pour attendre la terminaison des process

13/06/2019 - Ordonnancement 4

  • Correction de quelques bugs de desallocation des processus
  • Création des primitives chprio et getprio
  • bugfix pour passer les tests 1 à 4

14/06/2019 - Ordonnancement 5

  • bugfix pour passer les tests 5 à 8

Phase 4

13/06/2019 - File de Message 1

  • prototypage des files de messages

14/06/2019 - File de Message 2

  • implémentation des files de messages et debug

17/06/2019 - File de Message 3

  • debug et passages des tests 10 -> 16

Phase 5

Du 18/06/2019 au 20/06/2019

  • implémentation de la phase 5
  • Compréhension de l'empilement de la stack kernel et des switchs entre les ring 3 et 0, et les stacks.
  • Résolution d'un problème de wildcard dans un Makefile qui cassait des adresses hardcodées
  • Implémentation de 2 syscalls, exit et cons_write
  • Gestion du traitant d'interruption 49

21/06/2019

  • Implémentation des syscalls de queues de message et de gestion de processus
  • "Sécurisation" des appels systèmes : on vérifie que les paramètres des appels systèmes sont bien dans l'espace user.
  • Les appels systèmes font maintenant la sauvegarde des registres

Phase 6

24/06/2019

  • Implémentation des primitves cons_read, cons_echo
  • Divers debug

Phase 7

24/06/2019

  • Début de squelette de shell

Difficultés rencontrées

Avec les queues

Les queues du fichiers "queue.h" ne sont pas très simple à débuguer ! Pour afficher un elements de la queue, on utilise la commande suivant dans gdb :

p *(type_queue)((void *)queue.next-<entier>)

<entier> étant trouvé par la commande suivante :

p &((type_queue *)0)->link

GDB

gdb ne permet malheureusement pas de faire des reverse-step/reverse-next sur une target remote. Dommage !


Makefile coté user

Le wildcard *.S ne met pas forcément les fichiers dans l'ordre escompté. Un problème rencontré était que user_start ne se retrouvait pas à l'adresse 0x1000000. On y retrouvait à la place la première fonction défini dans un autre fichier *.S. (en l'occurence, le fichier syscalls.S) Pour fixer le problème, mettre tous les fichiers *.S dans le bon ordre dans le Makefile, en commençant par crt0.S.

Exit coté user

Il faut effectuer un mapping hard-codé de l'adresse de la fonction de retour coté user, afin que le kernel puisse y faire référence dans la primitive start. Nous avons effectué à peu près la même chose que présenté dans le squelette pour la fonction user_start. (cf les fichiers kernel.lds, crto0.S (coté user)) De plus, pour pouvoir bien récupérer la valeur de retour, il faut bien penser à ne pas écraser le registre EAX lors du retour des appels systèmes (mais seulement ceux qui retournent quelque chose !)

GCC

gcc essaye d'optimiser les valeurs de retours sur les registres EAX coté user. Cette optimisation peut engendrer des écrans bleus avec des adresses qui sont dans le range kernel mais qui n'ont pas de sens (0xFFFFE**). Ceci peut être résolu en ajoutant un flag -fno-pic dans KFLAGS du Makefile du répertoire user.

KFLAGS devrait ressembler à ceci:

KFLAGS=-Wall -Wextra -g -gstabs -pipe -std=c99 -nostdinc -fno-stack-protector -fno-pic

Divers

NixOS

Si comme l'un des auteurs de cette page vous êtes un aventurier sans pépin des distributions Linux, et que vous utilisez NixOS, je vous donne mon fichier shell.nix :

 with import <nixpkgs> {};
 stdenvNoCC.mkDerivation rec{
   name="projet-systeme";
   env = buildEnv { name = name; paths = buildInputs; };
   buildInputs = [
   gcc8
   pkgconfig
   gdb
   valgrind
   qemu
   python37
   pkgs.pythonPackages.pygments # syntax highlighting for gdb dashboard
 ];

}

Notez le stdenvNoCC qui permet d'override la version par défaut de gcc (7.4) de NixOS, qui compile avec des options assez poussées d'un point de vue sécurité ( '-D_FORTIFY_SOURCE=2','-fstack-protector-strong' 'ssp-buffer-size=4' '-fno-strict-overflow' '-Wformat=1' '-Wformat-security' ) ce qui empêche la compilation du projet. Du coup on utilise un gcc 8.3 avec moins d'options, et tout fonctionne.

Je recommande d'utiliser lorri, pour une intégration dans le terminal sans accroche.

Ressources

Pour gérer la filiation, utilisation de ce fichier de liste chainées : https://troydhanson.github.io/uthash/utlist.html

Pour le debug, cette page bien pratique : https://wiki.osdev.org/Exceptions

Une config bien sympa de gdb : https://github.com/cyrus-and/gdb-dashboard