Consignes

Ce premier TP est avant tout un TP d'introduction. Il ne donnera pas lieu à un compte-rendu ou une note. Les TPs 2 et 3 utiliseront MINIX et tout ce qui se trouve dans ce TP sera considéré comme acquis.

Liens utiles

1. Préliminaires : MINIX

MINIX ("Mini Unix") est à l'origine un système d'exploitation à but pédagogique. Depuis la version 3, il s'agit également d'un système d'exploitation de type UNIX (conforme à la norme POSIX) qui peut facilement être adapté et utilisé dans des systèmes embarqués. Même s'il s'agit d'un système « minimaliste » de type micro-noyau, il est possible d'installer toute une chaîne de développement (gcc, emacs, etc.) ainsi qu'un serveur X.

Démarrer Linux

Comme nous allons utiliser des outils UNIX, tout le TP se fera sous Linux (système principal) et MINIX (système émulé).

Commencez donc par démarrer Linux (et non pas Linux2) et vous logger.

1.1. Installation de MINIX avec QEMU

L'installation de MINIX est relativement aisée. Pour ne pas modifier la séquence de boot des ordinateurs de l'université, nous allons installer MINIX dans un « disque virtuel » (un fichier) et l'utiliser à travers un émulateur. Nous y perdrons en efficacité, mais y gagnerons en simplicité.

De plus, pour ne pas saturer vos comptes personnels (l'image du CD d'installation fait environ 300MB, et il faut compter environ 300MB pour pouvoir compiler un système confortablement), nous allons installer MINIX dans un répertoire temporaire.

Ceci signifie que votre installation de MINIX sera disponible uniquement sur l'ordinateur que vous utilisez pour le TP, et seulement jusqu'au prochain redémarrage.

Avant d'installer MINIX à proprement parler, il faut booter une machine virtuelle en utilisant l'image du CD d'installation. Tout ceci se fait de la manière suivante :

  1. création d'un répertoire TP-os dans /tmp/ :

    $ cd /tmp/
    $ mkdir TP-os
    $ cd TP-os
    

  2. vérification de l'espace libre restant sur le disque

    $ df -h
    
    La partition racine (montée sur /) doit contenir au moins 1G.

  3. vérification que QEMU est bien installé

    $ qemu
    

  4. téléchargement de l'image du CD d'installation dans le répertoire /tmp/TP-os/, soit par un navigateur (voir les liens au début du sujet), soit avec

    $ wget http://lama.univ-savoie.fr/~hyvernat/Enseignement/1011/info502/minix_R3.1.8-r8398.iso.bz2
    

  5. décompression de l'image

    $ bunzip2 minix_R3.1.8-r8398.iso.bz2
    

  6. vérification de l'empreinte de l'image

    $ md5sum minix_R3.1.8-r8398.iso
    
    Vous devez obtenir 0572a10df269831cc4f361252be56834

  7. création d'un disque virtuel minix.img de 300MB avec QEMU

    $ qemu-img create minix.img 300M
    

  8. « boot » de l'image du CD sur le disque virtuel grâce à QEMU

    $ qemu -localtime -net user -net nic -m 256 -cdrom minix_R3.1.8-r8398.iso  -hda minix.img -boot d
    
    Les arguments intéressants sont :

    • -m 256 pour spécifier 256MB de RAM à l'intérieur du système émulé
    • -cdrom minix_R3.1.8-r8398.iso pour dire que le fichier est l'image du CD contenu dans le lecteur de CD virtuel
    • -hda minix.img pour spécifier que le premier disque dur est représenté par le fichier minix.img
    • -boot d pour dire qu'il faut booter en premier sur le CD (avant d'essayer de booter sur le disque dur virtuel)

Si le boot sur le CD virtuel se passe bien, vous devriez obtenir une nouvelle fenêtre QEMU qui contient le système émulé. L'installation se fait maintenant en suivant les instructions du CD d'installation : loggez vous en tant qu'administrateur (root, sans mot de passe) puis lancer la commande

# setup

et suivez les instructions :

Vous pouvez ensuite éteindre la machine virtuelle :

# shutdown

et quitter QEMU :

cd>off

Si l'installation est très lente lors de la copie des fichiers (ça arrive parfois), vous pouvez remplacer l'option "-hda minix.img" de QEMU par "-drive file=minix.img,if=ide,media=disk,cache=writeback".

Félicitations, vous avez installé MINIX sur une machine virtuelle. Pour démarrer cette machine virtuelle, on utilise la commande

$ qemu -localtime -net user -net nic -m 256 -hda minix.img

(Notez qu'on ne déclare plus d'image pour le CD, et que l'ordre de boot est laissé vide car on démarre par défaut sur le premier disque dur.)

1.2. Tester et configurer MINIX pour ce TP

Lors du démarrage de MINIX, il est préférable de choisir le second système (2 Start Custom MINIX 3) car c'est le noyau qui est modifié par défaut. Le choix 1 reste (normalement) le système officiel.

Les premières étapes pour configurer MINIX seront :

  1. créer un mot de passe pour l'administrateur:

    # passwd
    
  2. créer un nouvel utilisateur « normal » et lui donner un mot de passe :

    Créez un utilisateur dans le groupe other et dont le répertoire personnel est dans /home/nom. La commande nécessaire s'appelle adduser...

    Quittez la session root et utilisez votre compte utilisateur...

Choisir un éditeur de texte

L'éditeur livré par défaut avec MINIX est ELLE. C'est un éditeur très simple, qui fonctionne en mode console sans interface graphique. Pour l'utiliser :

$ elle nom_de_fichier

et il suffit de connaître 4 commande :

Ceux qui préfèrent (et connaissent) peuvent également utiliser Vi. (Mais attention, il s'agit de Vi et nom de ViM, et il faudra probablement modifier la configuration par défaut pour que tout fonctionne comme vous le souhaitez.)

Commencez par éditer le fichier .ashrc dans votre répertoire personnel pour :

2. Les processus POSIX

  1. Écrivez un petit programme en C qui :
    • imprime un message,
    • se duplique (fork()) et affiche le PID du sous-processus créé
    • le processus créé affiche un message.
    Faites en sorte que vos processus ne terminent jamais, pour avoir le temps de faire la suite.

  2. Compilez et lancez votre programme en arrière plan :

    $ cc proc1.c -o proc
    $ ./proc1 &
    

  3. Vérifiez que vous avez bien créé 2 processus, et que l'un d'eux a le bon PID.

  4. Vérifiez que vous pouvez tuer le processus père ou le processus fils sans que l'autre ne disparaisse.

  5. Créez un processus zombie. (Vous pouvez vérifier qu'il existe en passant l'option l à ps.)

  6. Vérifiez qu'un processus zombie disparaît quand son père l'a « attendu ». Comment procédez ?

Pour ces questions, vous pouvez facilement insérer des sleep(1); dans votre programme pour attendre 1 seconde. Cela vous permettra de regarder l'évolution des processus...

  1. Écrivez un petit programme C qui génère un fils. Le fils se termine et le père génère un nouveau fils, ad infinitum.

    Que se passe-t'il ?

  2. Écrivez un petit programme C qui génère un fils et se termine. Son fils génère lui aussi un fils et se termine, ...

    Que se passe-t'il ?

  3. Écrivez un programme C qui génère un processus fils. Le père et le fils génèrent alors chacun un processus fils, etc.

    Que se passe-t'il ? À quoi ressemble l'arborescence des processus ?

3. Recompiler son premier noyau

Avant de recompiler votre premier noyau, je vous conseille de faire une copie des sources dans un répertoire :

$ su
# cd /usr/src/
# cp -r kernel/ kernel-bak
# cp -r servers/ servers-bak
# cp -r include/ include-bak

Pour recompiler un noyau, il faut :

Comme nous voulons garder plusieurs versions de notre noyau, il va falloir remplacer la dernière étape par :

# install -o root -m 600 ./image /boot/mon_noyau_a_moi-version-0
# edparams /dev/c0d0p0s0
c0d0p0s0> set
c0d0p0s0> name(3, "Mon nouveau noyau - 1") {image=/boot/mon_noyau_a_moi-version-0; boot}
c0d0p0s0> menu
[Esc]
c0d0p0s0> save
c0d0p0s0> exit

Ceci permet de rajouter un 3 troisième choix lors du démarrage de la machine virtuelle.

Recompilez un noyau en changeant le message de bienvenue du début :

Copyright 2010, Vrije Universiteit, Amsterdam, The Netherlands
...

De cette manière, vous pourrez vérifier que vous exécutez bien votre nouveau noyau.

4. Modifier le « process manager »

Les exemples précédents utilisent la fonction POSIX fork(). Cette fonction est gérée par le serveur « process manager » dans /usr/src/servers/pm/.

Trouvez le fichier correspondant et modifier le pour afficher un message lors de chaque exécution de fork().

Recompilez votre noyau et testez le. Qu'en pensez-vous ?

Proposez des solutions pour prévenir les « fork-bombs » de la section 2. Essayez de les implanter...

fork() n'est pas un appel système. Sous MINIX, l'architecture micro-noyau a pour conséquence qu'il y a très peu de vrais appels système. (Fonctions qui nécessitent le mode noyau.) Ce qu'il se passe ressemble à :

  1. un processus a besoin d'une opération bas-niveau,
  2. il fait une demande au noyau (sendrec),
  3. qui demande au serveur correspondant d'effectuer la tâche,
  4. le serveur répond au noyau,
  5. le noyaux répond au processus utilisateur.

Seules les étapes 2 et 4 se font en mode noyau.

Vous pouvez obtenir la liste des commandes qui vont nécessiter ceci en regardant dans le répertoire /usr/src/lib/libc/syscall. (Je crois...)

5. Ajouter une « backdoor » (facultatif)

À la fin du démarrage, le noyau demande au serveur init (dans src/servers/init/) d'ouvrir des consoles pour se logger. (MINIX en démarre trois par défaut, Linux en démarre six.) Ces consoles sont accessibles avec Alt et une touche Fonction. C'est la commande src/commands/login/login.c.

En modifiant les fichiers pertinents, créez une « back-door » dans votre noyau : il démarrera une version modifiée de login sur la neuvième console. Utiliser le login gulliver sur cette console devra vous donner accès au compte root, sans demander de mot de passe.