Customiser son initrd
Le 4 novembre 2008 à 02:49, 2ième version du billet (Extension pour les Debians).

La mise en oeuvre d'un kernel monolithique peut, à juste titre, rebuter par sa complexité. Et même si cette technique reste le meilleur moyen d'optimiser un kernel, il est malgré tout possible d'opter pour une approche plus douce en s'attaquant directement à initrd.

Historique (tout afficher)
  • v2 - Extension pour les Debians (2008-11-04 02:49)

Principe

Pour démarrer, un kernel a besoin de ses modules s'il veut espérer causer avec les périphériques. Le problème est que ces modules sont justement sur une périphérique, le disque dur, à laquelle le kernel ne sait accéder. Le coup classique de la poule et de l'oeuf en somme.

La première solution a été de construire un kernel monolithique contenant déjà les modules vitaux pour le démarrage. Mais de nos jours construire un tel kernel reviendrait à lui coller énormément de modules pour prendre en compte tous les cas possible (USB, IDE, SATA, EXT3, EXT4, REISER, etc.). Notre monolithe deviendrait donc trop gros et trop lourd pour tenir entièrement en mémoire.

La seconde solution, quasi universellement adoptée, est de procéder à un démarrage en deux temps. Dans le premier temps, GRUB va fournir au kernel un fichier, initrd (pour initial RAM disk), qui contient quelque binaires, un ensemble de modules vitaux, un interpréteur de script et un script de démarrage. Le kernel va alors monter ce fichier en mémoire pour en faire sa racine et ainsi pouvoir lancer le script. Ce dernier est alors chargé de trouver les bons modules pour la configuration matérielle courante, monter le vrai système de fichier, le définir comme nouvelle racine du kernel et exécuter le second script de démarrage qu'elle contient. Le fichier initrd est alors démonté et la mémoire occupée libérée.

L'image initrd se présente sous la forme d'un fichier dans le dossier /boot. Le nom qu'il peut avoir est variable d'une distribution à l'autre. Les initrd Mandriva sont par exemple nommés initrd-X.Y.Z.img que l'on peut automatiser pour le kernel courant en initrd-$(uname -r).img. Pour les Debians c'est plutôt initrd.img-$(uname -r)).

Pour ce qui est du format de ce fichier, c'est aujourd'hui une simple archive CPIO (une concurrent de tar) optionnellement compressée avec gzip qui est directement décompressée dans disque virtuel utilisant initramfs. Dans des temps plus reculés, il s'agissait d'un vrai système de fichier EXT2 ou cramfs monté en mode "loop".

Comme nous l'avons vu plus haut cette archive contient des modules (dans /lib/modules/$(uname -r)), des binaires (par exemple la gestion du splash), l'interpréteur de commande nash (qui n'est pas un shell !) et le script de démarrage /init

Ce qu'il faut bien comprendre à ce stade est que fondamentalement le couple kernel et initrd forme en mémoire un système Linux complet et opérationnel. Pour le rendre autonome, il suffirait juste de modifier le fichier /init pour qu'il exécute un shell de type busybox qui aurait été ajouté à l'archive. C'est d'ailleurs le système utilisé par Mandriva pour son mode Rescue présent sur les CD d'installation.

Dans le cas qui nous intéresse, le script init a juste pour travail de trouver les bons modules pour que le système puisse poursuivre son démarrage. On pourrait s'attendre à quelque chose d'évolué mais en réalité il s'agit simplement d'une série de chargement et d'attente de l'apparition des périphériques en /dev.

La première conséquence est que du coup un certains nombre de modules inutiles son chargés. Par exemple dans mon cas, le module piix alors que pata_piix a déjà pris en charge le contrôleur de disque.

La seconde conséquence est que le démarrage prend plus de temps pour rien. Par exemple initrd est capable de vous faire démarrer sur un disque USB, ce qui est très bien. Mais si vous ne le faites jamais, il va non seulement pré-charger toute la chaîne USB, mais aussi attendre un certain temps que d'éventuelles disques soient initialisés.

Enfin, dernière conséquence, certains modules génèrent des erreurs. Par exemple, encore sur l'U810, il tente d'utiliser le module generic_ide incompatible avec la plate-forme et échoue donc après quelques secondes.

L'objectif est donc de modifier l'image initrd de sorte à rendre le démarrage aussi efficace ou presque aussi rapide que pour un kernel monolithique.

Modification de l'image

De la modification de votre initrd peut résulter un système complètement inutilisable. Avant de poursuivre il est donc essentiel de créer une copie de sauvegarde de votre future ancienne image.

Pour cela, commencez par recopier votre initrd courant par un cp /boot/boot/initrd-$(uname -r).img /initrd-$(uname -r).backup.img (à adapter pour les autres distributions). Ensuite, dans /boot/grub/menu.lst, dupliquez le bloc correspondant au lancement de votre kernel, donnez-lui un nouveau nom, et modifiez la ligne correspondant au initrd pour pointer sur votre copie. cela doit au final donner quelque chose comme cela :

title Sauvegarde-2.6.27
kernel (hd0,0)/boot/vmlinuz-2.6.27-desktop-0.uc1mnb BOOT_IMAGE=2.6.27-desktop-0.uc1mnb root=/dev/sda1
initrd (hd0,0)/boot/initrd-2.6.27-desktop-0.uc1mnb.backup.img
fragment de /boot/grub/menu.lst

Ceci fait, relancez et testez que ce kernel alternatif fonctionne. Si c'est le cas, redémarrez à nouveau pour reprendre le kernel à modifier.

Maintenant que nous avons assuré nos arrières, nous pouvons commencer à hacker notre image en toute tranquillité. Pour cela, la première chose à faire est de la décompresser :

root#mkdir initrd
root#cd initrd
root#gzip -dc /boot/initrd-$(uname -r).img | cpio -id --no-absolute-filenames
15184 blocks
root#ls
bin/ dev/ etc/ firmware/ init* lib/ proc/ sbin@ sys/ sysroot/ usr/
root# 

L'image initrd est une archive cpio compressée. On utilise donc gzip pour décompresser vers la sortie standard puis cpio pour désarchiver le résultat. Cela se termine par une structure de fichiers qui rappelle celle d'une racine linux classique avec tous les modules dans /lib/modules/$(uname -r).

Lorsque le kernel effectue cette même opération au démarrage, il envoie tout cela sur un disque virtuel utilisant la mémoire comme espace de stockage (ramdisk). A ce stade, la racine est donc celle de ce disque et Linux exécute en premier lieu le script init qui s'y trouve.

C'est ce script qui contient les différents chargements de modules que nous allons chercher à modifier. Par exemple pour le cas "generic-ide" il faut commencer par localiser le code problématique :

# ...
echo "Loading ide_generic module"
modprobe -q ide_generic
# ...

Maintenant que nous tenons le coupable, il suffit simplement de commenter cette portion pour ne plus avoir l'erreur au démarrage. Ensuite nous pouvons recréer notre image.

# création et compression de l'image
root#find . | cpio --dereference -H newc -o | gzip -9 > /boot/initrd-$(uname -r).img
15184 blocks
root# 

La phase de recompression (gzip -9) est complètement optionnelle. Son but est de réduire l'espace disque occupé mais n'influe en rien sur l'espace pris en mémoire. Après c'est à vous de donner votre réponse à l'éternelle question : la compression prend du temps CPU pour rien ou la compression permet d'économiser des accès disques ?

Ceci fait, il faut redémarrer pour voir si tout fonctionne correctement.

Il est possible de continuer d'éliminer des portions de codes inutiles dans ce script. Par exemple on remarque qu'il y a toute une phase coûteuse en temps d'attente de stabilisation des périphériques USB. C'est sûrement très bien si l'on démarre sur un disque USB mais ne sert à rien dans le cas contraire. De même tous les modules USB sont chargés en mémoire : ehci-hcd pour l'USB 2.0, uhci-hcd pour le mode USB Host mais aussi ohci-hcd pour les périphériques USB 1.1. Personnellement je n'ai plus aucune périphérique en 1.1, je peux donc commenter ce chargement sans trop de crainte, cela m'évitera de la blacklister pour rien.

Dernier point, il est aussi possible, dans le dossier initrd, de supprimer directement des modules dans lib/modules/$(uname -r)/ pour éviter leur chargement. De la même manière vous pouvez ajouter des modules non prévus par votre distribution (un pilote disque particulier par exemple) et ajouter dans le script init la commande modprobe correspondante.

Automatisation

Le script d'initialisation initrd/init d'une distribution ne bouge pas tant que cela d'une mise à jour à l'autre du kernel. Il est peut donc être très pratique de dispose d'un script qui "patch" l'initrd courant avec nos modifications. C'est là que nous allons utiliser notre copie d'initrd faite plus haut.

# nous commençons dans le dossier initrd crée et peuplée plus haut
root#cd ..
root#mkdir initrd_old
root#cd initrd_old
root#gzip -dc /boot/initrd-$(uname -r).backup.img | cpio -id --no-absolute-filenames
root#cd ..
root#diff -Nautr initrd_old initrd > initrd.patch
root# 
création du patch

Maintenant que vous avez votre patch, il est possible de l'appliquer, lorsqu'une mise à jour de kernel est installée, par le script suivant :

#! /bin/sh
cp -f /boot/initrd-$(uname -r).img /boot/initrd-$(uname -r)-backup.img
mkdir initrd_new
cd initrd_new
gzip -dc /boot/initrd-$(uname -r).img | cpio -id
patch -p1 < ../initrd.patch
find ./ | cpio --dereference -H newc -o | gzip -9 > /boot/initrd-$(uname -r).img
rm -rf initrd_new
update_initrd.sh

Conclusion

La modification d'initrd m'a permis de faire descendre le démarrage du noyau à peu prés au même niveau que ce que j'obtenais avec un kernel monolithique, soit maintenant moins de 5 secondes

Cette méthode est sans aucun doute plus "bidouille" que la version pure et dure d'un paramétrage précis et recompilation du kernel mais présente le gros avantage d'être accessible à tous et surtout de permettre de continuer à profiter sans trop de manipulations des mises à jours d'une distribution.

Commentaires

tuxce , le 3 November, 2008 - 17:46

la commande:

gzip -dc /boot/initrd-$(uname -r).img | cpio -id

peut selon l'image initrd causer d'énormes soucis... sous mandriva, apparemment, c'est pas le cas, mais sous archlinux par exemple, l'image est compressée avec les chemins absolus et exécuter cette commande en root écrase les fichiers, il faut donc rajouter:

gzip -dc /boot/initrd-$(uname -r).img | cpio -id --no-absolute-filenames

pour complément d'informations (pour donner des nouvelles d'autres distribs Sticking out tongue), sous archlinux, le système de personnalisation de l'image initrd est prévue, c'est le paquet mkinitcpio qui s'en charge et la personnalisation de l'image s'effectue dans /etc/mkinitcpio.conf, et les scripts servant à faire tous pleins d'opérations se trouvent dans /lib/initcpio/hooks/

par contre, tu as un souci sur la mise en page je pense, il doit y avoir un manque avant cette partie:

C'est ce script qui contient les différents chargements de modules que nous allons chercher à modifier. Par exemple pour le cas "generic-ide" il faut commencer par localiser le code problématique :

(excellent le bloc avec le titre + les man des commandes Smiling)

Ulhume, le 3 November, 2008 - 20:18

@tuxce merci pour ces précisions. Continue à donner ce genre d'indication c'est important, surtout ces temps-ci, que la biodiversité ne se perde pas trop dans notre écosystème Wink

Pour info la commande que j'ai passé fonctionne sur toutes les dérivées de Redhat. A ce que j'ai compris, aussi sur celles de Debian mais la je suis moins sur.

ps: et merci pour les corrections Smiling

rodhia, le 3 November, 2008 - 22:49

Bravo pour l'article! Autant j'hésitais à me lancer dans la compilation d'un kernel monolitique, autant la customisation de l'initrd, présentée comme sa, semble simple! Je vais tester sa fiça! Encore merci pour tout ces excellents articles, sa éclaire la lanterne des néophytes Glad!

xtriade , le 3 November, 2008 - 23:38

Bonjour,
J'ai suivi le tuto avec interet , et essayé de l'appliquer chez moi. Je tourne avec une distro Ubuntu 8.04 . Une fois initrd.img.xxx decompresse , le dossier modules est vide !
Le script init ne fait rien d'extaordinaire , il fait appel à d'autres scripts .
Bref ca ne marche pas chez moi.
Merci

Ulhume, le 4 November, 2008 - 02:46

@xfce ton --no-absolute-filenames semble bien marcher aussi sur debian et mandriva, je l'ai mis par défaut du coup Smiling

tuxce , le 4 November, 2008 - 11:42

tu voulais dire tuxce (non xfce) Wink

Ulhume, le 4 November, 2008 - 02:48

@xtriade si ça marche mais tu ne regardes pas au bon endroit. Pour une raison que j'ignore y'a un dossier /modules vide dans les initrd d'Ubuntu. Mais les modules, comme c'est marqué dans le tuto, ne sont de toute façon pas là, mais dans ./lib/modules/$(uname -r)/

Sinon j'ai testé tout le tuto sur une ubuntu 8.04 et j'ai réussi sans problème à créer un initrd alternatif et à booter dessus. La seule différence est dans le nommage des fichiers initrd.

Ulhume, le 4 November, 2008 - 12:00

@tuxce OUPS Smiling)))

Poster un nouveau commentaire

Le contenu de ce champ est gardé secret et ne sera pas montré publiquement.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • To highlight piece of code, just surround them with <code type="language"> Your code &tl;/code>>. Language can be java,c++,bash,etc... Everything Geshi support.
  • Les lignes et les paragraphes vont à la ligne automatiquement.
  • Textual smileys will be replaced with graphical ones.
  • Les adresses de pages web et de messagerie électronique sont transformées en liens automatiquement.

Plus d'informations sur les options de formatage

Connexion utilisateur
Les derniers bavardages...