BusyBox est un programme qui combine, dans un seul binaire, la plupart des utilitaires que on utilise sur son système hôte Linux, et pas seulement ceux de CoreUtils. Ceux de BusyBox sont des remplaçants minimalistes dans le sens où toutes les options ne sont pas présentes. BusyBox a été écrit pour l'embarqué, il est donc optimisé en taille et n'utilise que très peu de mémoire vive.
La configuration de BusyBox est réalisée à l'aide d'un menu, comme pour le noyau Linux ou µClibC :
bash# make menuconfig
Celui-ci est très bien fait et nous pouvons naviguer selon différents thèmes. Comme on peut le constater, les utilitaires clones des CoreUtils ne sont effectivement qu'une petite partie de BusyBox :
General Configuration Build Options Installation Options Archival Utilities Coreutils Console Utilities Debian Utilities Editors Finding Utilities Init Utilities Login/Password Management Utilities Miscellaneous Utilities Linux Module Utilities Networking Utilities Process Utilities Another Bourne-like Shell System Logging Utilities Linux System Utilities Debugging Options
Pour la démonstration, je vais utiliser la configuration par défaut, il faudra l'adapter par la suite. Dans un premier temps, il faut préciser quel compilateur est utilisé :
Build Options : [*] Do you want to build BusyBox with a Cross Compiler? (powerpc-linux-uclibc-) Cross Compiler prefix
Ensuite, il faut déterminer où sera installé BusyBox :
Installation Options : (/mnt/ramdisk) BusyBox installation prefix
Il ne reste alors plus qu'à compiler et à installer BusyBox (avec le RAMDisk monté) :
bash# make bash# make install
Pour concevoir un système Linux complet, il faut au minimum :
- un noyau Linux
- une collection d'outils
- un répertoire
/dev
, où sont stockés les fichiers de périphériques - un répertoire
/etc
, où sont stockés les fichiers de configuration - un répertoire
/lib
, où sont stockées les bibliothèques partagées
Nous allons voir dans cette section quels sont les fichiers à placer dans les répertoires /etc
, /dev
, /lib
.
Le répertoire /dev
contient tous les fichiers spéciaux de périphériques, c'est à dire les points de contacts entre les programmes utilisateurs et les pilotes du noyau Linux. Le contenu de ce répertoire est donc indispensable.
Il peut paraître difficile de savoir quels sont les fichiers à ajouter à /dev
. Si on veut en avoir une idée, on peut lancer la commande suivante sur les binaires du système embarqué (BusyBox dans cette démonstration), qui donne la liste des chaînes de caractères contenant le nom de ce répertoire :
bash# powerpc-linux-uclibc-strings busybox | grep "/dev/" | sort -u [...] /dev/console /dev/loop%d /dev/null /dev/ptmx /dev/random /dev/root /dev/tty [...]
Il existe plusieurs solutions pour remplir le répertoire /dev
, chacune ayant leurs qualités et leurs défauts :
- copier le répertoire
/dev
de notre hôte pour le mettre sur la cible, mais cela gaspillerait beaucoup trop de place et surtout utiliserait beaucoup trop d'I-nodes (celles-ci contiennent les informations sur les fichiers et sont en nombre très limité). - utiliser des scripts tels que
/dev/MAKEDEV
, mais on risque d'avoir le même problème que précédemment. Il se peut même qu'au final il n'y ait pas les fichiers spéciaux nécessaires. - utiliser le système de fichiers virtuels DevFS. Ce dernier crée automatiquement les fichiers spéciaux dans
/dev
en fonction des périphériques supportés par le noyau. Cela aurait très bien pu être la solution idéale, mais il existe des bogues qui ne peuvent pas (techniquement) être résolus et malheureusement le code de DevFS n'est plus maintenu à jour (DevFS vient d'être retiré du noyau au moment où j'écris ces lignes). Il existe une alternative :udev/hotlug
, mais elle n'est disponible que pour les noyaux 2.6. - créer les fichiers manuellement avec mknod. Cela nécessite de connaître le numéro de majeur/mineur de chaque entrée. On peut s'aider du fichier
Documentation/devices.txt
disponible dans les sources du noyau Linux.
Pour cette démonstration j'utiliserai DevFS (mais je ne le recommande pas, au contraire : il est beaucoup plus rigoureux de la faire à le main). Il ne faut surtout pas oublier de créer le répertoire /dev
et d'activer le DevFS dans le noyau Linux :
bash# mkdir /mnt/ramdisk/dev
Le répertoire /etc
contient tous les fichiers de configuration du système, comme par exemple la configuration du réseau, du serveur FTP, ...
Comme précédemment, on peut s'aider de la commande suivante pour connaître quels sont les fichiers utiles :
bash# powerpc-linux-uclibc-strings busybox | grep "/etc/" | sort -u [...] /etc/filesystems /etc/fstab /etc/group /etc/hosts /etc/init.d/rcS /etc/inittab /etc/issue [...]
Je vais créer un petit système pour illustrer ce qu'il faut mettre dans ce répertoire. Commençons par le fichier /etc/fstab
, qui définit les systèmes de fichiers accessibles par le système :
none /proc proc defaults 0 0 none /dev devfs defaults 0 0
Le répertoire /proc
servira de point de montage pour le système de fichiers virtuels ProcFS, très utile puisqu'il contient beaucoup d'informations sur le système et qu'il sert de configuration à chaud du noyau Linux. Il ne faut donc pas oublier de le créer :
bash# mkdir /mnt/ramdisk/proc
A présent, nous allons voir le script de démarrage lu par le processus init
. Il faut savoir que l'init
de BusyBox ne gère pas les niveaux d'exécution, et que par conséquent il les ignore. Pour le fichier /etc/inittab
, je vais faire très simple puisqu'il ne contiendra que deux lignes, l'une pour définir le script de démarrage système, l'autre pour définir le programme qui sera lancé par défaut (et qui sera relancé s'il s'arrête) :
::sysinit:/etc/init.d/rcS ::respawn:-/bin/sh
Il ne reste plus qu'à écrire le script de démarrage système, nommé précédemment /etc/init.d/rcS
:
#! /bin/sh /bin/mount -a
Il ne faut surtout pas oublier de le rendre exécutable :
chmod +x /mnt/ramdisk/etc/init.d/rcS
On n'a pas besoin du répertoire /lib
lorsque l'on compile les programmes en statique, c'est à dire avec l'option -static
de gcc ou de ld. En effet, toutes les fonctions utilisées par les programmes compilés statiquement sont intégrées dans leurs binaires, ils n'ont alors pas besoin des bibliothèques partagées (les fameux fichiers .so
).
Un tel système peut prendre beaucoup de place, puisque les fonctions de base sont dupliquées d'un programme à l'autre. Il est donc souvent préférable d'utiliser des bibliothèques partagées. Nous allons voir dans cette section comment remplir efficacement le répertoire /lib
.
Pour savoir quelles sont les bibliothèques nécessaires dans un binaire (BusyBox pour cette démonstration), il suffit de lancer la commande suivante :
bash# powerpc-linux-uclibc-readelf -d busybox | grep "Shared library" 0x00000001 (NEEDED) Shared library: [libcrypt.so.1] 0x00000001 (NEEDED) Shared library: [libc.so.6]
Il ne faut pas oublier de relancer cette commande sur les bibliothèques affichées, jusqu'à ce que toutes les dépendances soit trouvées. Il existe une commande qui permet de trouver toutes les dépendances d'un seul coup (ldd), mais elle ne marche que sur la cible (puisqu'elle est compilé pour la cible et pas pour l'hôte)...
Revenons-en à l'installation de ce système dynamique. En plus d'avoir besoin de ces bibliothèques partagées, il faut l'éditeur de lien dynamique : /lib/ld.so
. Tout cela se trouve dans le répertoire $SYSROOT/lib
:
bash# mkdir /mnt/ramdisk/lib bash# cp -a $SYSROOT/lib/ld* /mnt/ramdisk/lib/ bash# cp -a $SYSROOT/lib/libc* /mnt/ramdisk/lib/
Il ne reste alors plus qu'à charger le RAMDisk avec le noyau et à vérifier le système, comme sur cette capture faite sur une console série :
BusyBox v1.00 (2005.06.30-14:49+0000) Built-in shell (ash) Enter 'help' for a list of built-in commands. /bin/sh: can't access tty; job control turned off # ./lib/ld.so.1 --list /bin/busybox libcrypt.so.1 => /lib/libcrypt.so.1 (0x0ffb3000) libc.so.6 => /lib/libc.so.6 (0x0fe56000) /lib/ld.so.1 => ./lib/ld.so.1 (0x08000000)