Bootfähige Linux-CDs sind nicht nur in Notfällen äußerst praktisch. Wer sie selbst herstellen will, braucht allerdings etwas Know-how über den Bootvorgang oder das hier vorgestellte Tool – besser noch beides.
Rettungsdisketten gibt es wie Sand am Meer. Doch sie haben alle eine Reihe von Nachteilen. Sie sind stets zu klein, sie sind langsam und sie sind fehleranfällig. Bei einigermaßen modernen PCs braucht man sie auch gar nicht mehr, da man direkt von der CD booten kann. Was liegt also näher, als sich selbst eine bootbare Linux-CD zu basteln?
Wer jetzt sagt, so was gibt es doch schon, hat natürlich Recht (siehe Infos-Kasten). Doch oftmals fehlt auf diesen CDs gerade ein ganz spezielles, unbedingt benötigtes Programm. Außerdem gibt es noch andere Gründe, eine eigene CD zu basteln. Ideal sind solche CDs beispielsweise für Vorführungen, Schulungen oder Kiosk-Systeme. Oder um dem besten Freund mal stolz die eigene brandneue KDE-Installation auf seinem Rechner vorzuführen.
In diesem Artikel wird ein einfach zu bedienendes Verfahren vorgestellt, mit dessen Hilfe sich mit ganz geringem Aufwand eine funktionierende Linux-Installation auf eine bootbare CD übertragen lässt. Die zunächst folgenden Abschnitte bringen jedoch erst einmal etwas Theorie zum Bootvorgang selbst, die aber auch unabhängig vom Thema interessant sind. Davon ausgehend wird anschließend die Verwendung des Build-Systems zur Erzeugung der bootbaren CD beschrieben.
Aller Anfang ist leicht – der Bootvorgang
Nach dem Einschalteten des Rechners sucht dieser an im Bios festgelegten Stellen nach ausführbarem Code. Normalerweise sind das die Diskette, das CD-ROM-Laufwerk sowie die erste Festplatte. Dieser ausführbare Code ist sehr einfach, da zu diesem Zeitpunkt noch keine Betriebssystemmittel zur Verfügung stehen, insbesondere kein Filesystem. Seine Aufgabe ist es, den Betriebssystem-Kern zu laden und zu starten.
Dieser einfache Code ist auch im Linux-Kernel enthalten, deshalb kann man den Kernel direkt auf eine Diskette kopieren ( dd if=bzImage of=/dev/fd0) und ihn davon starten. Der Kernel initialisiert dann alle Teilsysteme und startet anschließend die Programm-Datei /sbin/init auf der Root-Partition (genau genommen werden folgende Dateien in dieser Reihenfolge gesucht: /sbin/init, /etc/init, /bin/init und /bin/sh).
Die Root-Partition wird beim Kompilieren des Kernels festgelegt (im Toplevel Makefile) und hat per Default denselben Wert wie die aktuelle Root-Partition, auf der die Kompilierung läuft. Nachträglich kann dieser Wert mittels rdev(8)-Utilities geändert werden. Wer an den Details des Bootvorgangs interessiert ist, sollte unbedingt einen Blick in die Datei /usr/src/linux /init/ main.c werfen.
Das Programm /sbin/init ist der Urprozess eines laufenden Linux-Systems (es hat die Prozess-ID 1). Es liest seine Konfigurationsdatei /etc/inittab und startet gemäß den Einträgen entsprechende Skripte und Gettys (oder den Xdm für grafische Logins).
Der Nachteil des beschriebenen Verfahrens ist seine mangelnde Flexibilität. Die Root-Partition liegt fest, außerdem können keine zusätzlichen Parameter an den Kernel übergeben werden. Deshalb kommt in der Praxis fast ausschließlich ein zweistufiges Verfahren zum Einsatz. Statt den Kernel direkt zu starten, lädt das Bios einen so genannten Bootloader. Der lädt anschließend den Kernel und übergibt an ihn die Argumente – entweder aus einer Konfigurationsdatei oder von einer Kommandozeile. Die gängigen Bootloader (Lilo, Chos, Grub und andere) können noch mehr: es sind Bootmanager, mit ihnen können auch unterschiedliche Betriebssysteme beziehungsweise Kernel geladen werden.
Wo sind die Files – die Initial Ramdisk
Selbst mit dem Bootloader bleibt eine Frage ungeklärt: Bei einem gänzlich neuen System gibt es keine formatierte Root-Partition, also auch kein Filesystem mit /sbin/init und /etc/inittab. Der gerade erfolgreich gestartete Kernel würde also mit einer Kernel-Panik stehen bleiben. Die Lösung für das Problem ist eine so genannte Initial Ramdisk. Dabei handelt es sich um ein Linux-Filesystem, das entweder vom Kernel selbst (klassische Ramdisk) oder vom Bootloader in den Speicher geladen wird (Initial Ramdisk: initrd). Die typische Notfalldiskette enthält also genau zwei Komponenten: einen Kernel und eine gezippte Datei, die ein komplettes Dateisystem enthält.
Verwendet man einen Bootloader, sind zwei Argumente für den Kernel notwendig: root=/dev/ram und initrd=Pfad zur Datei. Ohne Bootloader muss man den Kernel (wieder mit Hilfe von rdev) patchen, um die Startadresse der Ramdisk festzulegen. Das letzte Verfahren ist aber inzwischen ziemlich ungebräuchlich, da in diesem Fall sowohl der Kernel als auch die Ramdisk auf eine rohe Diskette an die richtigen Offsets zu kopieren sind.
Der Bootvorgang läuft damit etwas modifiziert ab. Zuerst lädt der Bootloader den Kernel und die Initial Ramdisk. Der Kernel entpackt sie zu einer normalen Ramdisk und mountet sie als Root-Filesystem. Anschließend wird – falls vorhanden – die Datei /linuxrc ausgeführt.
Wenn dieses Programm fertig ist, wird wie oben beschrieben die richtige Partition als Root-Partition gemountet und anschließend /sbin/init aufgerufen. Vorher wird die Initial Ramdisk entweder per umount aus dem Dateisystem ausgehängt (und der Speicherplatz wieder freigegeben) oder – falls das /initrd-Verzeichnis existiert – nach /initrd umgehängt.
Bestandsaufnahmen mit Linuxrc
Dreh- und Angelpunkt jeder Erstinstallation ist das Programm Linuxrc. Es kann ein Shell-Skript sein, ist aber bei den großen Distributionen in der Regel ein sehr aufwändiges C-Programm und für die Partitionierung, Auswahl und Installation der Pakete verantwortlich.
Für eine bootbare CD-ROM muss Linuxrc vor allem drei Dinge tun: je nach vorhandener Hardware die richtigen Module laden, ein CD-ROM-Laufwerk mit der Boot-CD finden und den Kernel davon überzeugen, dass das entsprechende Device die richtige Root-Partition ist. Letzteres ist aber sehr einfach. Linuxrc muss nur die Device-Nummer (sie setzt sich aus Major- und Minor-Nummer zusammen) der Root-Partition in die Datei /proc/sys/kernel/real-root-dev schreiben.
Damit das alles funktioniert, muss der Kernel sowohl mit Ramdisk Support als auch Initrd-Support konfiguriert und kompiliert sein. Die Default-Größe von Ramdisks hat sich übrigens in einem der letzten Kernel geändert und beträgt nur noch 4 MByte. Das kann sowohl bei der Kernel-Konfiguration als auch über einen Kernel-Bootparameter zur Laufzeit geändert werden.
Erzeugen der Initial Ramdisk
Es gibt verschiedenen Wege, eine Initial Ramdisk zu erzeugen. Im abgedruckten Listing sind die notwendigen Schritte aufgeführt. Zuerst wird ein Ram-Device mit Nullen vorbelegt, dann ein Dateisystem angelegt. Danach mountet man das Ram-Device ganz normal und kopiert alle benötigten Daten in das gemountete Verzeichnis. Den Inhalt des gesamten Device kopiert man dann mittels dd und gunzip komprimiert in eine Datei. Je nachdem, ob nur eine Startdiskette entstehen soll oder ein komplettes Rettungssystem, ist der Inhalt der Disk sehr einfach oder entsprechend umfangreich.
Im Fall eines Rettungssystems ist auch die Größe so zu optimieren, dass man neben dem Kernel jedes Byte ausnutzt. Ein bekannter Trick dabei ist es, ein Programm zu schreiben, das sich unterschiedlich verhält, je nachdem, mit welchem Programmnamen man es aufruft: Wird es als cat aufgerufen, verhält es sich wie cat und so weiter. Die einzelnen Programmaufrufe sind dann nur noch harte Links auf dieses Programm.
Das spart viel Platz, weil der Startup-Code, den jedes Programm braucht, nur noch einmal einzutragen ist. Der Nachteil dabei ist, dass man nicht einfach ein paar Programme löschen kann, um Platz für ein eigenes Tool auf der Ramdisk zu schaffen.
Das Devfs-Filesystem |
|
Devfs ist wie Proc ein virtuelles Dateisystem, das vom Kernel gleichzeitig mit dem Root-Dateisystem gemountet werden könnte. Der große Nachteil von Devfs ist, dass die meisten Programme damit nicht umgehen können. So liefert SuSE zwar ein (noch nicht einmal sauber ablaufendes) Devfs-Kernel-Patch für die 7.0 mit, aber Yast kann mit einem laufenden Devfs-System nicht umgehen. Devfs soll aber ab Kernel 2.4 als Option dabei sein, also wird sich an dieser Stelle wohl einiges ändern. Die Distributoren müssen ja davon ausgehen, dass die Systeme mit Devfs laufen. Die Boot-Skripte von Red Hat 7 sind darauf schon vorbereitet. Das Prinzip von Devfs ist einfach. Anstatt die Geräte über Major- und Minor-Nummern zu identifizieren, wie das gegenwärtig der Fall ist, meldet sich jeder Treiber (etwa beim Laden des entsprechenden Moduls) explizit an und bekommt dann seinen Namen zugeteilt. Im Gegensatz zu heutigen Systemen, bei denen man locker auf über 2000 angebliche Geräte unterhalb von /dev kommt, erscheinen beim Devfs dort nur die tatsächlichen Geräte. Die Vorteile sind offensichtlich: ein übersichtliches, strukturiertes /dev-Verzeichnis mit sprechenden Namen (wer weiß denn schon, was /dev/hdj13 wirklich bezeichnet), keine Verwaltung von Major- und Minor-Nummern mehr (die sind nötig, damit sich nicht mehrere Module in die Quere kommen) sowie die Unterstützung von Hot-Plug-fähigen Geräten. Um Devfs gab es wahrscheinlich die größten Flame-Wars der Kernel-Geschichte und lange Zeit war es nur als inoffizielles Patch verfügbar. Die Gegner führten insbesondere an, dass der Kernel durch die zusätzliche Verwaltung der Geräte größer wird. Umso überraschender war die Tatsache, dass im Laufe der letzten Entwicklerserie (Kernel 2.3.x) Linus Torvalds das Devfs doch in den offiziellen Kernel übernommen hat, allerdings mit der Kennung “experimental”. Devfs löst auf generische Weise ein Problem, das Subsysteme wie USB ebenfalls zu lösen haben. Und auch High-End-Geräte mit im laufenden Betrieb austauschbaren PCI-Geräten verlangen nach einer Lösung, die es Geräten erlaubt, sich an- und wieder abzumelden. Um ein System sauber mit Devfs zu fahren, müssten es alle verwendeten Treiber unterstützten. Das ist generell aber noch nicht der Fall, deshalb gibt es einen Devfs-Daemon, der bei Zugriff auf die klassischen Device-Namen diese in die Devfs-Namen umsetzt. Wahrscheinlich wird es in der 2.5er Kernelserie zu einer Konsolidierung des gesamten Problemkreises kommen, da es wenig Sinn macht, dynamische Geräte an mehreren Stellen im Kernel zu unterstützen. |
Eine CD-ROM als Root-Verzeichnis
Eine CD-ROM als Root-Verzeichnis hat natürlich den Vorteil der Größe, aber den großen Nachteil gegenüber einer Ramdisk, dass sie nur Lese-Zugriffe erlaubt. Leider benötigt ein laufendes Linux-System auf viele verschiedene Verzeichnisse Schreib-Rechte, teilweise schon während der Startphase:
Eine denkbare Lösung wäre es, unter all diese Verzeichnisse je eine Ramdisk zu hängen, ein Dateisystem anzulegen und Inhalte von der CD reinzukopieren. Aber schnell erkennt man, dass dies Probleme bereitet. So soll der Kernel die CD als Root-Partition mounten, doch unter /dev existieren noch keine Devices, sie sollen ja erst in eine Ramdisk aus Inhalten von der CD erzeugt werden. Ähnlich geht es mit dem Verzeichnis /etc. Das Programm /sbin/init liest die /etc/inittab, aber auch hier gibt es noch keine Verzeichnisse und Dateien, da erst das erste durch /sbin/init gestartete Skript die Dateien und Verzeichnisse anlegen könnte.
Eine Ramdisk fürs /var
Auch wenn es so nicht geht, ist der Ansatz doch nicht völlig falsch und für /var, /tmp und /home sehr brauchbar. Um nicht drei Ramdisks zu erzeugen und damit einiges an Verschnitt zu haben, wird allerdings /tmp durch einen symbolischen Link auf /var/tmp ersetzt, analog auch /home durch einen Link auf /var/home. Das Erzeugen der Ramdisk, der Mount unterhalb von /var und das Einspielen einer kompletten Verzeichnishierarchie (aus einem Tar-Archiv) erfolgt dabei so früh wie möglich, nachdem /sbin/init die Kontrolle an das erste Bootskript übergeben hat (bei SuSE beispielsweise ist es das Skript /sbin /init.d/boot).
Listing 1: Erzeugen einer Ramdisk |
1: dd if=/dev/zero of=/dev/ram bs=1k count=2048 2: mke2fs -vm0 /dev/ram 2048 3: mount /dev/ram /mnt 4: cp -a foo/* /mnt 5: dd if=/dev/ram bs=1k count=2048 | gzip -v9 > ramdisk.gz |
Der /proc/mounts-Trick
Fürs /etc brauchen wir jedoch eine andere Lösung. Hier kann man den Trick anwenden, die Datei /etc/mtab durch einen symbolischen Link auf /proc/mounts zu ersetzen. Die letzte Datei enthält zwar nicht alle Informationen wie die mtab, aber immer noch genug, um normal arbeiten zu können. Wer sich beide Dateien mit cat ausgeben lässt, sieht kaum einen Unterschied.
Durch diesen Trick kann also /etc auf der CD bleiben. Werden für weitere Dateien ebenfalls Schreib-Rechte benötigt, so könnte man sie durch symbolische Links auf Dateien unterhalb von /var ersetzen, etwa ln -s /var/etc/foo/etc/foo.
Das /dev-Problem
Als letztes verbleibendes Verzeichnis macht uns /dev zu schaffen. Dafür gibt es noch keine vollständig zufriedenstellende Lösung. Eine sehr effiziente Möglichkeit ist es, das Devfs-Filesystem zu verwenden. Was es damit auf sich hat, ist im gleichnamigen Kasten näher erläutert.
Da der Aufwand, ein System zumindest zum ersten Mal mit Devfs zum Laufen zu bringen, ziemlich groß ist, kommt in diesem Projekt eine Alternative zum Einsatz. Sie ist zwar hinsichtlich des Speicherverbrauchs nicht optimal, aber dafür ohne Manipulation an der Installation einsetzbar. Sie nutzt die Tatsache, dass die Initial Ramdisk wie oben beschrieben auf das Verzeichnis /initrd umgehängt wird, falls dieses Verzeichnis existiert. Das geschieht als letzte Aktion, bevor das neue Root-System gemountet wird. Ersetzt man nun /dev auf der CD durch einen symbolischen Link auf /initrd/dev, hat man alle Devices, die schon auf der Initial Ramdisk verfügbar waren.
Die dadurch entstandene Situation ist etwas pathologisch. Der Mount der CD verwendet ein Device der Initial Ramdisk. Diese ist wiederum auf einem Verzeichnis der CD gemountet. Der Effekt ist, dass es keine Möglichkeit gibt, während des Herunterfahrens des Systems einen sauberen Abbau der Dateisysteme vorzunehmen. Und weil Linux gemountete CD-ROMs sperrt, kommt man nur nach dem Abschalten wieder an die CD heran.
Die bootbare CD
Nach diesem Ausflug in die Untiefen des Bootens bleibt nur noch die Zusammensetzung der Puzzleteile zu einer bootbaren CD. Was die Sache wieder vereinfacht, das ist die Tatsache, dass eine bootbare CD nach dem El-Torrito-Standard nichts weiter tut, als eine Diskette zu emulieren. Zu diesem Zweck erstellt man also eine Diskette mit Bootloader, Kernel und Initial Ramdisk (die im Wesentlichen nur das oben beschriebene spezielle Linuxrc enthält), kopiert die Diskette in eine Datei (etwa dd if=/dev/fd0 of=bootdsk.img) und sagt dem Brennprogramm, welche Datei die Diskettenemulation ist.
Unter Linux ist jedoch der letzte Satz nicht ganz richtig. Das eigentliche Brennprogramm Cdrecord erstellt nämlich keine CD-Dateisysteme (so genannte ISO9660-Dateisysteme), dafür ist das Programm Mkisofs zuständig. Es erstellt das Filesystem und kopiert gleichzeitig alle Dateien hinein, die man auf der CD haben will. Das Resultat ist eine maximal 650 MByte große Datei, die von Cdrecord über einen CD-Brenner auf ein Medium übertragen wird (Details dazu finden Sie im Brenner-Test in diesem Heft-Schwerpunkt).
Bei einem fehlerfreien Bios spielt die Wahl des Bootloaders keine Rolle, da die gerade bootende CD eine bootende Diskette emuliert. Dummerweise ist aber nicht jedes Bios fehlerfrei, so kommt es zu dem Effekt, dass zwar der Lilo von der CD geladen wird, er aber über Bios-Aufrufe den Kernel von einer realen statt der emulierten Diskette laden will. Deshalb hat sich die Verwendung von Syslinux als Bootloader für bootbare CDs durchgesetzt. Dieser Loader benötigt ein (offenbar unsterbliches) DOS-Filesystem auf der Diskette und findet dadurch den Kernel nicht direkt über das Bios.
Make macht das Leben leichter
Damit ist der Weg zu einer bootbaren Linux-CD bereits aufgezeigt. Man muss nur ein paar Verzeichnisse und Dateien durch symbolische Links ersetzen, ein kleines Linuxrc-Programm schreiben, eine Bootdiskette erzeugen und das Ganze auf CD brennen. Leider ist ein System mit all den umgehängten Verzeichnissen schwer zu pflegen. Insbesondere das Umbiegen von /dev kann ein paar unangenehme Seiteneffekte haben, wenn man das System, das als Vorlage gedient hat, noch einmal von der Platte booten will.
Da aber fast alle Schritte zu einer bootbaren CD von der verwendeten Distribution unabhängig sind, liegt es nahe, ein Makefile für die Automation zu erzeugen. Aus einem Makefile wurde beim Ausführen der Idee allerdings eine ganze Hierarchie, doch das Prinzip ist gleich geblieben. Man braucht dazu einen Rechner, der Platz genug für zwei Linux-Systeme hat: ein aktives System für die Arbeit und ein zweites System, das als Vorlage für die CD dient.
Das Vorlagensystem wird ganz normal installiert und konfiguriert. Natürlich muss man sich etwas beschränken, denn auch 650 MByte sind schnell belegt. Das Vorlagensystem wird dann von der zweiten Partition aus bearbeitet. Dabei ist der Vorgang zweigeteilt. Im ersten Schritt werden nur jene Veränderungen vorgenommen, die nicht destruktiv in dem Sinne sind, dass das System anschließend nicht mehr booten kann. So ist beispielsweise das Verschieben von /home nach /var/home völlig unproblematisch. Dagegen werden potenziell destruktive Operationen quasi on the fly während der CD-Erstellung durchgeführt.
Wer mit dem Konzept etwas rumspielen will, kann sich die Dateien von [1] herunterladen. Eine ausführliche Dokumentation ist beigefügt. Zwar ist das System noch nicht perfekt, aber die Grundfunktionen stehen schon bereit. Übrigens gibt es auch professionelle Systeme (zum Beispiel Webpads), die mit bootbaren Linux-CDs arbeiten. Ein Update des Systems ist damit auch für Laien kein Problem, einfaches Austauschen einer CD erledigt das System-Update.
Wer ein Faible für coole Gadgets hat, kann sich auch noch CD-Rohlinge im Visitenkartenformat besorgen. Die sind zwar bei knapp 20 MByte zur Verfügung stehendem Platz recht teuer, aber für ein persönliches Linux-Rettungssystem aus der Hosentasche reicht es locker. ( uwo)
Infos |
|
[1] Bernhards bootable Linux-CD: http://www.bablokb.de/bblcd/ [2] H.P.Anvin: The most overfeatured rescue disk ever created: http://www.kernel.org/pub/dist/superrescue/ [3] Homepage von Gibraltar, einem Firewall-System, das von einer CD gestartet werden kann: http://gibraltar.vianova.at/ [4] Die bootbare Visitenkarte von Innominate: http://www.innominate.de/level2.phtml?parent=101 [5] Ein CD-basiertes Rettungssystem: http://rescuecd.sourceforge.net/ [6] Der Klassiker. Mehr passt nicht auf eine Diskette: http://www.toms.net/rb/home.html |
Der Autor |
|
Bernhard Bablok arbeitet bei der AGIS mbH (Allianz Gesellschaft für Informatik Service mbH) als Systemprogrammierer im Systemmanagement-Bereich. Wenn er nicht Musik hört, mit dem Radl oder zu Fuß unterwegs ist, beschäftigt er sich mit Themen rund um Objektorientierung. Er ist zu erreichen unter: coffee-shop@bablokb.de |





