12.3. Booten mit der initial ramdisk

12.3.1. Problemstellung

Sobald der Linux-Kernel geladen und das Root-Dateisystem (/) gemountet ist, können Programme ausgeführt und weitere Kernel-Module eingebunden werden, um zusätzliche Funktionalitäten bereitzustellen. Um aber das Root-Dateisystem überhaupt mounten zu können, müssen verschiedene Bedingungen erfüllt sein: Der Kernel benötigt die entsprechenden Treiber, um das Gerät ansprechen zu können, auf dem das Root-Dateisystem liegt (insbesondere SCSI-Treiber). Weiter muss der Kernel den Code enthalten, der benötigt wird, um das Dateisystem lesen zu können (ext2, reiserfs, romfs usw.). Weiterhin ist es denkbar, dass bereits das Root-Dateisystem verschlüsselt ist. Zum Mounten ist in diesem Fall die Eingabe des Schlüssels/Passworts erforderlich.

Betrachtet man nur einmal das Problem der SCSI-Treiber, so sind verschiedene Lösungsansätze denkbar: Der Kernel kann alle denkbaren Treiber enthalten. Dies ist problematisch, da sich die verschiedenen Treiber beißen können. Außerdem wird der Kernel dadurch sehr groß. Eine andere Möglichkeit besteht darin, verschiedene Kernel zur Verfügung zu stellen, die jeweils nur einen oder sehr wenige SCSI-Treiber enthalten. Auch dieser Weg ist problematisch, da er eine sehr große Zahl unterschiedlicher Kernel notwendig macht. Ein Problem, das durch verschieden optimierte Kernel (Athlon-Optimierung, SMP) noch weiter verschärft wird.

Der Ansatz, den SCSI-Treiber als Modul zu laden, führt zur generellen Problematik, der durch das Konzept der initial ramdisk begegnet wird: Das Bereitstellen einer Möglichkeit, Userspace-Programme bereits vor dem Mounten des Root-Dateisystems ausführen zu können.

12.3.2. Konzept der initial ramdisk

Die initial ramdisk (auch initdisk oder initrd genannt) löst genau diese oben beschriebenen Probleme. Der Linux-Kernel bietet die Möglichkeit, ein (kleines) Dateisystem in eine Ramdisk zu laden, und darin Programme ausführen zu lassen, bevor das eigentliche Root-Dateisystem gemountet wird. Das Laden der initrd wird dabei vom Bootloader (GRUB, LILO usw.) übernommen; all diese Bootloader benötigen lediglich BIOS-Routinen, um Daten vom Bootmedium zu laden. Wenn der Bootloader den Kernel laden kann, kann er auch die initial ramdisk laden. Spezielle Treiber sind somit nicht erforderlich.

12.3.3. Ablauf des Bootvorgangs mit initrd

Der Bootloader lädt den Kernel und die initrd in den Speicher und startet den Kernel, wobei der Bootloader dem Kernel mitteilt, dass eine initrd vorhanden ist und wo im Speicher diese liegt. Ist die initrd komprimiert (was typischerweise der Fall ist), so dekomprimiert der Kernel die initrd und mountet sie als temporäres Root-Dateisystem. Hierauf wird in der initrd ein Programm mit dem Namen linuxrc gestartet. Dieses Programm kann nun all die Sachen tun, die erforderlich sind, um das richtige Root-Dateisystem mounten zu können. Sobald linuxrc terminiert, wird die (temporäre) initrd wieder abgehängt unmounted und der Bootvorgang wie gewohnt mit dem Mounten des richtigen Root-Dateisystems fortgeführt. Das Mounten der initrd und das Ausführen von linuxrc kann somit als ein kurzes Intermezzo während eines normalen Bootvorgangs betrachtet werden. Der Kernel versucht nach dem Booten der tatsächlichen Root-Partition, die initrd auf das Verzeichnis /initrd umzumounten. Wenn das fehlschlägt, weil zum Beispiel der Mountpunkt /initrd nicht vorhanden ist, wird der Kernel versuchen, die initrd abzuhängen. Sollte auch dies fehlschlagen, ist das System zwar voll funktionsfähig, jedoch kann der durch die initrd belegte Speicher nie freigegeben werden; er steht somit nicht mehr zur Verfügung.

12.3.3.1. Das Programm linuxrc

Für das Programm linuxrc in der initrd gibt es lediglich die folgenden Anforderungen: Das Programm muss den speziellen Namen linuxrc tragen und im Root-Verzeichnis der initrd liegen. Abgesehen davon muss es lediglich vom Kernel ausgeführt werden können. Das bedeutet, dass linuxrc durchaus dynamisch gelinkt sein darf. In diesem Fall müssen natürlich die shared libraries wie gewohnt vollständig unter /lib in der initrd verfügbar sein. Weiter darf linuxrc auch ein Shellskript sein. In diesem Fall muss natürlich eine Shell in /bin existieren. Kurz gesagt, muss die initrd ein minimales Linux-System enthalten, das die Ausführung des Programmes linuxrc erlaubt. Bei der Installation von SUSE LINUX wird ein statisch gelinktes linuxrc verwendet, um die initrd so klein wie möglich halten zu können. linuxrc wird mit root-Rechten ausgeführt.

12.3.3.2. Das echte Root-Dateisystem

Sobald linuxrc terminiert, wird die initrd abgehängt und verworfen, der Bootvorgang geht normal weiter und der Kernel mountet das wirkliche Root-Dateisystem. Was als Root-Dateisystem gemountet werden soll, kann durch linuxrc beeinflusst werden. Dazu muss linuxrc lediglich das /proc-Dateisystem mounten und den Wert des echten Root-Dateisystems in numerischer Form nach /proc/sys/kernel/real-root-dev schreiben.

12.3.4. Bootloader

Die meisten Bootloader (vor allem GRUB, LILO und syslinux) können mit initrd umgehen. Die einzelnen Bootloader werden wie folgt angewiesen, eine initrd zu verwenden:

GRUB

Eintrag der folgenden Zeile in /boot/grub/menu.lst:

initrd (hd0,0)/initrd

Da die Ladeadresse der initrd in das bereits geladene Kernel-Image geschrieben wird, muss der Befehl initrd auf den kernel-Befehl folgen.

LILO

Eintrag der folgenden Zeile in /etc/lilo.conf:

initrd=/boot/initrd

Die Datei /boot/initrd ist die initial ramdisk. Sie kann komprimiert sein.

syslinux

Eintrag der folgenden Zeile in syslinux.cfg:

append initrd=initrd

Weitere Parameter können in der Zeile folgen.

12.3.5. Anwendung von initrd bei SUSE

12.3.5.1. Installation des Systems

Die initrd wird bereits seit geraumer Zeit für die Installation verwendet: Bei manueller Installation kann der Anwender in linuxrc Kernel-Module laden und die für eine Installation notwendigen Eingaben vornehmen. linuxrc startet dann YaST, das die Installation durchführt. Hat YaST seine Arbeit getan, teilt es linuxrc mit, wo das Root-Dateisystem des frisch installierten Systems liegt. linuxrc schreibt diesen Wert nach /proc und führt dann einen Reboot durch. Danach startet YaST erneut und installiert die restlichen Pakete bereits in jenes System, das gerade installiert wird.

12.3.5.2. Booten des installierten Systems

In der Vergangenheit hat YaST mehr als 40 Kernel für die Installation im System angeboten, wobei sich die Kernel im Wesentlichen dadurch unterschieden, dass jeder Kernel einen bestimmten SCSI-Treiber enthielt. Dies war nötig, um nach dem Booten das Root-Dateisystem mounten zu können. Weitere Treiber konnten dann als Modul nachgeladen werden.

Da inzwischen aber auch optimierte Kernel zur Verfügung gestellt werden, ist dieses Konzept nicht mehr tragbar – es wären inzwischen weit über 100 Kernel-Images nötig.

Daher wird nun auch für das normale Starten des Systems eine initrd verwendet. Die Funktionsweise ist analog zu einer Installation. Das hier eingesetzte linuxrc ist jedoch einfach nur ein Shellskript, das lediglich die Aufgabe hat, einige vorgegebene Module zu laden. Typischerweise handelt es sich nur um ein einziges Modul, nämlich denjenigen SCSI-Treiber, der benötigt wird, um auf das Root-Dateisystem zugreifen zu können.

12.3.5.3. Erstellen einer initrd

Das Erstellen einer initrd erfolgt mittels des Skripts mkinitrd (früher mk_initrd). Die zu ladenden Module werden bei SUSE LINUX durch die Bezeichner INITRD_MODULES in /etc/sysconfig/kernel festgelegt. Nach einer Installation wird diese Variable automatisch durch die richtigen Werte vorbelegt (das Installations-linuxrc weiß ja, welche Module geladen wurden). Die Module werden in genau der Reihenfolge geladen, in der sie in INITRD_MODULES auftauchen. Das ist besonders wichtig, wenn mehrere SCSI-Treiber verwendet werden, da sich ansonsten die Benennung der Platten ändern würde. Streng genommen würde es reichen, nur denjenigen SCSI-Treiber laden zu lassen, der für den Zugriff auf das Root-Dateisystem benötigt wird. Da das automatische Nachladen zusätzlicher SCSI-Treiber jedoch problematisch ist, laden wir alle bei der Installation verwendeten SCSI-Treiber mittels der initrd.

[Important]Wichtig

Da das Laden der initrd durch den Bootloader genauso abläuft wie das Laden des Kernels selbst (LILO vermerkt in seiner map-Datei die Lage der Dateien), muss bei der Verwendung von LILO nach jeder Änderung der initrd der Bootloader neu installiert werden – bei der Verwendung von GRUB ist dies nicht notwendig!

12.3.6. Mögliche Schwierigkeit – Selbstkompilierte Kernel

Übersetzt man sich selbst einen Kernel, so kann es zu folgendem Problem kommen: Versehentlich wird der SCSI-Treiber fest in den Kernel gelinkt, die bestehende initrd bleibt aber unverändert. Beim Booten geschieht Folgendes: Der Kernel enthält bereits den SCSI-Treiber, die Hardware wird erkannt. Die initrd versucht nun jedoch, den Treiber nochmals als Modul zu laden. Dies führt bei einigen SCSI-Treibern (insbesondere beim aic7xxx) zum Stillstand des Systems. Streng genommen handelt es sich um einen Kernelfehler (ein bereits vorhandener Treiber darf nicht ein zweites Mal als Modul geladen werden können) – das Problem ist bereits im Zusammenhang mit seriellen Treibern bekannt.

Es gibt mehrere Lösungen: Entweder den Treiber als Modul konfigurieren (dann wird er korrekt in der initrd geladen) oder aber den Eintrag für die initrd aus /etc/grub/menu.lst bzw. /etc/lilo.conf entfernen. Äquivalent zur letzteren Lösung ist es, den Treiber aus INITRD_MODULES zu entfernen und mkinitrd aufzurufen, das dann feststellt, dass keine initrd benötigt wird.

12.3.7. Ausblick

Für die Zukunft ist denkbar, dass eine initrd für weitaus mehr (und anspruchsvollere) Dinge verwendet wird als nur für das Laden der Module, die für den Zugriff auf / benötigt werden.

  • Root-Dateisystem auf Software RAID (linuxrc setzt die md-Devices auf)

  • Root-Dateisystem auf LVM

  • Root-Dateisystem ist verschlüsselt (linuxrc fragt nach Passwort)

  • Root-Dateisystem auf einer SCSI-Platte am PCMCIA-Adapter

12.3.7.1. Weitere Informationen

  • /usr/src/linux/Documentation/initrd.txt

    (Nur verfügbar, wenn die Kernel-Quellen installiert wurden)

  • Die man-page zu initrd.