Cet article décrit comment nous avons réalisé une distribution linux tenant sur une disquette et ayant pour but de lire un cd audio.
Au démarrage, un programme - le bootstrap loader - recherche un secteur d'amorçage. S'il trouve un secteur d'amorçage, il le charge en mémoire et passe ainsi le controle au programme qui charge le système d'exploitation en mémoire. Le premier élément qui est chargé est le noyau du système. Celui-ci se charge en mémoire et lance le premier processus à partir du système de fichier (racine du périphérique). Celui-ci obtient ces informations à partir du fichier inittab, et lance donc une série de commande permettant, à partir du répertoire /etc, de charger les différents niveaux de démarrage. Lorsque les processus nécessaires au bon fonctionnement du système sont lancés, une invite de commande apparaît.
Le projet se base donc sur la création d'un système sur le même principe de fonctionnement, en rajoutant l'exécution directe d'un serveur de son et d'un lecteur de CD. Cela rajoute donc non seulement la recherche, la compilation « spéciale » et la mise en place de programmes de petite taille, mais également une reconnaissance dans les niveaux de démarrage inférieurs (par exemple les modules de son pour le noyau).
Il s'agit ici d'une explication simplifiée, mais elle résume bien ce qui se passe.
La lecture d'un CD se fait en plusieurs étapes comme il est précisé dans la partie précédente. Le principe est de mettre en premier lieu les modules nécessaires à disposition du noyau (drivers de carte son, plus ou moins en fonction du degré de portabilité du système). Il est également important d'utiliser un serveur de son (comme alsa, art, etc...). Enfin il faut également utiliser un lecteur. Pour cela, et étant donné que le projet se base sur un mode texte exclusivement (l'utilisation d'un X entraîne presque obligatoirement l'explosion de la taille du support, donc pas de xmms...), il faut utiliser un utilitaire non seulement qui fonctionne en mode texte, mais qui prend peu de place. Celui qui a été choisi est Workbone
Comme on peut s'en rendre compte ici, il semble à première vue bien difficile de placer tout cela sur une simple disquette. Et c'est bien là que réside toute la difficulté : rendre des utilitaires petits mais sans perdre un fonctionnement efficace.
Pour faire fonctionner un système, quel qu'il soit (sur diquette ou sur disque dur), il est important d'en connaître les composantes essentielles. Ici sont détaillés les plus importantes.
La création d'un noyau adapté aux besoins du projet est une partie incontournable du projet. Il est en effet important de considérer que le noyau, base du système, permet l'interaction matériel/logiciel. Il doit donc être compilé préalablement avec les options requises. La difficulté réside également dans le choix des modules et composants natifs (réduction de la taille du noyau pour respecter les contraintes de taille du support).
Quelques options du noyau doivent impérativement être mise en place, comme par exemple l'option Ram disk avec pour valeur 4096. Cette étape étant très importante car elle détermine la taille du système de fichier que l'on va créer. Cette taille ne sera pas la taille réelle du fichier à copier sur la disquette puisqu'il sera compressé.
Le noyau possède une séquence d'opérations particulières qu'il effectue lors de son auto-chargement.
La première chose qu'il détecte est la présence de périphériques ; il tente ensuite de décompresser le système de fichiers, afin de repérer et exécuter le programme init. Il s'agit du premier processus lancé, et qui se charge de lancer tous les autres processus, par pallier : des niveaux de démarrage existent (qui apparaissent sous le nom de rc.d0, rc.d1, etc... pour une distribution Mandrake par exemple). Ici ces répertoires (et donc les niveaux d'exécution) ne nous sont pas utiles, étant donné que le système disposera de très peu de processus à lancer au démarrage.
Le programme init obtient généralement les noms des proccessus qu'il doit exécuter dans le fichier inittab. Il a donc fallut créer un fichier inittab. Ce fichier étant beaucoup plus simple que celui d'une distribution classique puisqu'il ne gère que quelques preocessus : Il lance un shell pour chaque terminal virtuel, gère le CTRL+ALT+SUPPR comme si l'on avait tapé la commande reboot, et enfin, il doit lancer le montage de /proc.
Le choix de mettre le maximum de composants en natif à été fait. Certains composants du noyau sont obligatoires ou ne nécessitent pas l'utilisation de modules. Les premiers problèmes rencontrés étaient conséquents à des oublis de composants natifs essentiels, comme les fichiers de caractères ASCII (codepage ISO).
De plus le noyau effectue une certaine séquence d'opérations à la fin de son auto-chargement, comme la location ou la présence de certains fichiers, ce qui rajoute d'autres fichiers impératifs au système de fichiers.
Les seuls composants qu'il était nécessaire de placer en modules, sont le son. En effet, plusieurs modules de son sont utilisables, qui corespondent aux différents drivers de cartes son et aux différentes normes utilisables (comme le son 5.1 par exemple). Il a été choisi de les placer en tant que modules pour éviter que le noyau les décompresse dès le démarrage et donc utilise de l'espace RAM inutilement; il ne doit utiliser que les modules dont il a réellement besoin (dépendant de la carte son du PC hôte).
Initrd est le nom du système de fichier qui est utilisé sur la disquette. Le principe est de le construire à partir d'une arborescence et de fichiers nécessaires à une utilisation minimaliste, de le compresser et de le placer sur la disquette. Lors du boot du noyau, celui-ci le cherchera et le décompressera à partir d'une option placée dans le fichier de configuration d'un utilitaire permettant de rendre une disquette « bootable » (voir aussi parties Ram-disk et Syslinux).
Etant donné le peu de place disponible sur la disquette, il est très important de repérer en premier lieu les fichiers obligatoires, et éviter toute perte de mémoire inutile. Ici sont détaillées quelques explications sur certains fichiers dont on ne peut pas se passer;
Le système de fichier doit comprendre une arborescence de répertoires classique ( /etc, /dev, /lib, /mnt, /mnt/fd0, /bin, /usr, ...). Ces répertoires vont permettre au noyau de repérer les emplacements où sont localisés les fichiers dont il a besoin.
Le répertoire /etc contient les scripts permettant de gérer le système. Contrairement à un système classique, le contenu de ce répertoire est assez simple. En effet, seuls certains scripts sont présents. Le contenu de ces fichiers est en annexe.
Les répertoires /bin et /lib contiennent respectivement la busybox et la libc que nous verrons tout à l'heure.
De façon à intéragir avec les différents périphériques, le système de fichier doit contenir des fichiers spéciaux : des fichiers blocs et caractères. Ces fichiers sont créés grâce à la commande mknod. Cette commande demande deux numéros représentant le numéro majeur et mineur du fichier. Chacun de ces numéros représente un périphérique. Par exemple les numéros 2:0 représente le lecteur disquette.
Certains ne sont pas nécessaires, d'autres obligatoires ; voilà la liste des fichiers incontournables :
Le ramdisk est un fichier spécial qui permet d'accueuillir le système de fichier décompressé à partir de la disquette. Le ramdisk est une sorte de disque dur en mémoire : il émule l'utilisation d'un disque mais en virtuel.
Pour créer un ram-disk, voici une commande possible :
L'option F permet de forcer le formatage, l'option m0 permet qu'aucun espace ne soit réserver pour l'utilisateur root.
Le noyau, après chargement, va décompresser le système de fichiers et le placer sur ce disque virtuel placé en mémoire. Pour cela, le noyau doit connaître l'emplacement où trouver ce fichier; ceci est donné par l'option root=/...
Syslinux est un programme utilitaire permettant de rendre un disque formaté en FAT bootable. Il suffit simplement de copier un noyau et un système de fichier compressé sur la disquette, de créer un fichier de configuration permettant de passer quelques options lors du boot au noyau et à l'exécutable de syslinux (fichiers de message, nom du noyau, options du noyau...). (Voir annexe). Cet utilitaire est très simple à installer et sa configuration aisée
Voici le script contenu dans le fichier syslinux.cfg :
Comme on peut le voir, les options passées au noyau sur la ligne append sont nécessaires : la première lui spécifie dans quel fichier se trouve le système de fichier. La deuxième lui permet de trouver la racine du système (le ram-disk), et la troisième est le chemin d'accès au programme init.
Etant donné la taille de la libc classique (environ 1.2 Mo), il a fallu trouver une biliothéque statique plus petite. La micro-libc permet d'avoir les mêmes fonctions que la libc mais en prenant beaucoup moins de places (environ 150 ko).
Pour utiliser la micro-libc, il faut la compiler afin de choisir quelle fonction étaient nécessaires, dans notre cas par exemple, nous avons pu supprimer la composante réseau de la micro-libc.
L'utilisation de la micro-libc oblige la recompilation chaque application utilisée par le système cible avec la micro-libc ainsi qu'avec un gcc et un make (eux aussi compilé avec la micro-libc). Car si nous n'avions pas fait cela, les programmes aurait utilisé par défaut la libc.
Heureusement, il existe des sytèmes tout fait contenant gcc et make. L'essentiel du projet portait donc de recompiler la busybox et les utilitaires.
La busybox est un utilitaire qui remplace la plupart des fonctions de /bin et /sbin. L'important était de la recompiler en ajoutant et supprimant les fonctions nécessaires au projet. Par exemple certains utilitaires sont indispensables pour au moins tester le fonctionnement du système par exemple : ls, ln, cat ... Certains autres n' étaient pas utiles, comme ceux permettant de gérer le support réseau.
La busybox fonctionne de la façon suivante :
Son fonctionnement est assez simple, et elle peut être trouvée pré-compilée pour la micro-libc. Pour des soucis de taille, il était important de la recompiler.
Un des premiers problème rencontré était la version syslinux de Mandrake, qui était patchée (en fait une version inutilisable), qui ne permettait pas une bonne gestion du système de fichier. Le problème était que le syslinux chargeait convenablement le noyau, mais ne chargeait à aucun moment le système de fichier. Ce qui entrainait donc un « kernel panic » lorsque le noyau se lancait. la version utilisée a été téléchargée sur le site internet du logiciel.
Le problème le plus important est survenue après avoir réussi l'auto-chargement du noyau. Ce dernier doit décompresser le système de fichier et lancer le programme init, ou en tout cas le programme qui lui est apparenté (par l'option « init=... »). Malheureusement, cela n'est pas une mince affaire. Le fait qu'il ne trouve ce premier processus à lancer entraîne un « kernel panic », non expliqué et donc difficilement résolvable.
Ce problème était lié au fait que le système de fichier utilisé était défectueux. Ceci étant du à l'utilisation de la µ-libc.Cette dernière ayant probablement été mal utilisée (voir ci-dessous).
Il s'agit du dernier problème rencontré. Cette µ-libc aurait permis d'économiser une énorme place sur le système de fichier. Cependant, nous n'avons pas réussis à la mettre en place correctement, ce qui nous a d'ailleurs causée plusieurs erreurs lors du chargement du système de fichier. Par manque de temps, nous nous sommes donc résigné à utiliser une simple libc 2.9 plus petite que la 6 (environ 900ko).
Suite à la perte de mémoire sur notre disquette due à l'utilisation de la libc 2.9, nous ne pouvions plus utiliser le noyau que nous avions compilé précédement. N'ayant pas réussis à en recompiler un suffisament petit. Nous avons substitué à notre noyau 2.6.1 de 750ko par un noyau de 500ko précompiler.