Einführung in den Bootloader Grub

Winfried Mueller, www.reintechnisch.de, Start: 09.09.03, Stand: 21.11.07

1. Konzept

Wer es eilig hat, überspringe diesen Teil. Wer nur Dinge tun möchte, die er verstanden hat, sollte hier weiterlesen.

Ein System zu booten, ist gar nicht so einfach. Denn zu Anfang hat man nur begrenzte Möglichkeiten, auf das System zuzugreifen. Im BIOS einer jeden X86er Maschine ist fest verankert, dass nach dem Selbsttest ein Bootloader an einem ganz bestimmten Platz gesucht wird. Der Code, der dort steht, wird dann einfach ausgeführt, ihm wird die weitere Kontrolle übergeben. Als Platz hat man sich den ersten Sektor einer Festplatte oder Diskette ausgesucht. Genau genommen hat man gerade mal 446 Bytes im ersten Sektor zur Verfügung, in die man ein Miniprogramm reinlegen kann, welches das System startet.

Ein System zu starten bedeutet unter Linux, den Kernel in den Speicher zu laden und diesem dann die Kontrolle zu übergeben.

Wer schon einmal ein kleines C-Programm geschrieben hat, weiß, dass 446 Bytes nun wirklich extrem wenig Raum für ein Programm sind. Deshalb sind Bootloader auch meist in Assembler geschrieben. Aber auch da ist das noch zu wenig Raum, um Code abzulegen, der z.B. ein ext2 Filesystem lesen kann.

Lilo geht den Weg, sich gar nicht um Dateisysteme zu kümmern. Nach der Installation eines Kernels legt es eine map-Datei an, in der genau steht, auf welchen Sektoren auf der Platte sich der Kernel befindet. Es kann ja durchaus sein, dass der Kernel sich in vielen kleinen Stücken auf der Platte verteilt befindet. Das Dateisystem setzt diese zerstreuten Sektoren zu einer Datei zusammen. Lilo muss das für sich eigenständig tun, weil es noch nicht die Intelligenz besitzt, mit Dateisystemen umzugehen. Über den Trick einer zuvor erzeugten map-Datei weiß lilo also, wo es den Kernel findet.

Der Nachteil eines solchen Verfahrens ist, dass immer dann, wenn der Kernel sich irgendwie auf der Platte verschiebt, lilo eine neue map-Datei erstellen muss. Verschiebung meint, dass sich der Kernel nun auf anderen Sektoren befindet, obwohl er im Dateisystem immer noch an der gleichen Stelle sein kann. Genau dies ist der Fall, wenn man einen neuen (auch gleichlautenden) Kernel erzeugt, oder diesen löscht und wieder neu reinkopiert. Gleiches kann auch passieren, wenn man eine Platte defragmentiert. Viele Bootprobleme im Zusammenhang mit lilo drehen sich um diese Eigenart, die Anfänger gewöhnlich so nicht erwarten.

Die Bemühungen waren deshalb groß, einen Bootloader zu schreiben, der intelligenter ist, der nach Möglichkeit alle gängigen Dateisysteme lesen kann. Eigentlich hätte so eine Unternehmung scheitern müssen, weil man das nicht in 446 Byte unterbringen kann.

Eine erste Idee des Grub-Projektes war, in den Bootloaderbereich zuerst einmal ein kurzes Stück Programm zu schreiben (stage1 genannt), welches ein längeres Programmstück nachlädt (stage2) genannt. Dieses wird mit dem gleichen Mechanismus auf einem Dateisystem gefunden, wie es lilo tut - mit einem map-File, in dem genau steht, auf welchen Sektoren stage2 zu finden ist. Und dieses stage2 Programm kann dann mit allen bekannten Dateisystemen umgehen und den Kernel dann über einen Dateisystemzugriff finden und laden. Wir haben es hier also mit einem zweistufigen System zu tun.

Ist es nicht egal, ob ich nun direkt den Kernel über ein map-File lade (wie bei lilo) oder erst den umständlichen Weg über stage2 gehe? Stage2 ist komfortabler, da dieses File eigentlich nur einmal installiert und dann nie mehr verändert wird. Kernels werden aber öfters verändert und ausgetauscht. Dafür brauche man dann aber kein neues map-File zu erzeugen.

So ganz befriedigend war das aber immer noch nicht, zumindest musste für stage2 ein map-File erzeugt werden und Probleme tauchten auch hier immer wieder auf, dass Anwender das nicht bedachten. Nun kam jemand auf eine trickreiche Idee. Es ist nämlich gewöhnlich so, dass eine ganze Reihe Sektoren auf der ersten Spur einer jeden Festplatte nicht genutzt werden. Bei fast allen modernen Systemen kann man sich darauf verlassen, dass das so ist. Und genau diese Sektoren werden benutzt, um nun ein stage1_5 unterzubringen, ein mittelgroßes Programmstück, was zumindest schon in der Lage ist, ein bestimmtes Dateisystem anzusprechen. Dieses Programmstück kann dann stage2 direkt via Dateisystemzugriff nachladen, es braucht also kein map-File mehr für stage2. Es braucht überhaupt kein map-File mehr, weil stage1_5 ja immer an fester Stelle, außerhalb eines Dateisystems untergebracht ist. Das ist genial und vereinfacht die ganze Angelegenheit erheblich. Denn nun können sich die Programme sektorbezogen beliebig auf der Platte verschieben, grub funktioniert weiterhin. Das ist z.B. der Fall, wenn man ein Backup der Systempartition neu einspielt.

Man bedenke, dass all diese Klimmzüge nur deshalb nötig sind, weil wir mit dem Altballast umgehen müssen, der noch aus den Anfängen der Personalcomputer stammt. Das ist übrigens ganz häufig so. Würde man alles neu erfinden, wäre es viel einfacher. Bis dann die nächsten Anforderungen wieder neue Schwachstellen zu Tage fördern. Und so muss man immer versuchen, Altes zu integrieren.

Grub besteht also aus 3 Programmteilen, stage1, stage1_5 und stage2. Man kann auch heute noch sowohl den Weg stage1, stage2 wählen, wie auch über stage1_5 mehr Komfort haben. Nur bei ganz wenigen Systemen funktioniert stage1_5 nicht. Es gibt mehrere stage1_5, für jedes Dateisystem eins, weil man es aus Platzgründen nicht geschafft hat, alle Dateisysteme darin unterzubringen. Bei der Installation wählt man genau das stage1_5 aus, welches mit dem Dateiystem kompatibel ist, von wo stage2 nachgeladen wird. Grub handelt das automatisch.

Neben diesen 3 Programmteilen besteht grub noch aus der menu.lst, eine zentrale Konfigurationsdatei, in der man alle möglichen Einstellungen und Einträge fürs Bootmenü findet. Alles, was zu grub gehört, ist gewöhnlich fein säuberlich in einem extra Verzeichnis in /boot/grub untergebracht.

Genug, es reicht, was den theoretischen Hintergrund angeht. Wer noch nicht satt ist, kann in [1]-[3] weiterlesen.

2. Installation

2.1 Paket installieren

Erstmal gilt es, grub auf der Distribution seiner Wahl zu installieren. Unter debian tut das ein `apt-get install grub`, ein nachfolgendes update-grub erzeugt schonmal ein nettes Grundgerüst für die Konfigurationsdatei menu.lst. Andere Distris werden ebenfalls irgendein Grundgerüst für die menu.lst haben. Unter /boot/grub sollte jetzt alles zu finden sein, also die menu.lst und die stage-Programme.

2.2 Erstellen der Konfiguration in menu.lst

Wir müssen jetzt erstmal anfangen, die menu.lst zu bearbeiten. Hier ein Listing einer typischen menu.lst:

 
01 default    0
02 timeout    30  
03 color cyan/blue white/blue
04 
05 title           Speichertest memtest86
06 root            (hd0,1)
07 kernel          /boot/memtest86.bin
08 
09 title           Hauptsystem mit Kernel 2.2.20
10 root            (hd0,1)
11 kernel          /boot/vmlinuz-2.2.20 root=/dev/hda2 ro
12 
13 title           Hauptsystem mit Kernel 2.4.18
14 root            (hd0,1)
15 kernel          /boot/vmlinuz-2.4.18-586tsc root=/dev/hda2 ro hdc=ide-scsi
16 initrd          /boot/initrd.img-2.4.18-586tsc
17
18 title           Notfall Linux-System
19 root            (hd0,4)
20 kernel          /boot/vmlinuz-2.4.18-bf2.4 root=/dev/hda5 ro
21
22 title           Win98 - eine Designstudie
23 root            (hd0,0)
24 chainloader     +1

01 - Der erste Menüeintrag soll der Defaulteintrag sein, der beim Booten selektiert ist und ggf. auch automatisch gestartet wird. In diesem Fall wäre das "Speichertest memtest86"

02 - Starte automatisch nach einem timeout von 30 Sekunden

03 - Auswahl ein paar netter Farben für das Bootmenü. Du darfst wählen...

05..07 - Erster Booteintrag. Ich habe hier das Speichertestprogramm memtest86 gewählt, welches auch automatisch nach 30 Sekunden gestartet wird. Falls also mal der Strom ausfällt, fährt das Hauptsystem nicht wieder unkontrolliert hoch, was mitunter Sinn macht. Memtest86 ist ein eigenständiges Programm, was ohne Betriebssystem auskommt und deshalb direkt wie ein Kernel in den Speicher geladen wird.

09-11 - Ein 2.2er Kernel wird geladen. Für diesen ist kein initrd nötig. Initrd ist ein kleines Linuxsystem, was beim booten vom Kernel benutzt wird, um schonmal grundlegende Dinge zu tun, damit das Hauptsystem hochgefahren werden kann, z.B. module zu laden, damit spezielle Geräte angesprochen werden können. Die meisten neueren Distris benutzen ein initrd, der alte 2.2er Kernel von Debian woody hier braucht keins. "title" ist der Titel, der später im Menü angezeigt wird. "root" gibt die Festplatte an, wo grub den Kernel suchen soll. hd0 steht für die erste Festplatte (hda) und die 1 steht für die zweite Partition (0..n).

Hier eine Tabelle mit den Zuordnungen:

SystemkomponenteGrub-DeviceLinux-Device
1. Floppyfd0/dev/fd0
2. Floppyfd1/dev/fd1
1. Festplattehd0/dev/hda
2. Festplattehd1/dev/hdb
3. Festplattehd2/dev/hdc
1. Festpl., 1.Partitionhd0,0/dev/hda1
1. Festpl., 2.Partitionhd0,1/dev/hda2
2. Festpl., 1.Partitionhd1,0/dev/hdb1

(Auch SCSI-Festplatten werden über hdx,x angesprochen. Man muss dann herausfinden, welche Reihenfolge das BIOS vergeben hat.)

Die Syntax verlangt, dass der Wert in Klammern steht. "kernel" ist nun der Eintrag, wo angegeben wird, wie der Kernel heißt und wo er zu finden ist. Und natürlich, welche Startparameter der Kernel mitbekommt.

13-16 - Ein 2.4er Kernel wird geladen, hier mit initrd.

18-20 - Hierbei handelt es sich um ein weiteres Notfall-Linux-System auf dem Rechner, welches völlig unabhängig vom Hauptsystem läuft. Es braucht kein initrd und liegt auf hda5.

22-24 - Hier wird ein recht unbekanntes Betriebssystem geladen. Einen Kernel gibt es hier nicht, vielmehr steht der Startcode im zweiten Sector von hda1. Hierfür gibt es den Eintrag "chainloader +1", der also ab dieser Stelle das Programm lädt, welches dann die Kontrolle bekommt. Man lädt hier sozusagen einfach einen weiteren Bootloader, der sich dann um alles kümmert. Alle Windows- und DOS-Systeme werden in dieser Art gestartet.

Das war's schon, die Konfiguration ist wirklich einfach. Wer mehr will, bemühe [1].

2.3 Installation in den Bootsektor

Jetzt muss grub noch einmalig in den Bootsektor installiert werden. Unter Linux kann man grub direkt als Kommando aufrufen und gelangt dann in die Grub-Shell bzw. Grub-Kommandozeile.

Eine andere Möglichkeit ist der Start von einer grub-Bootdiskette. So eine Bootdiskette ist an sich eine gute Idee und man sollte sie immer in seiner Werkzeugbox haben. Denn hierüber hat man Zugriff auf die Grub-Shell, ohne ein System hochfahren zu müssen. In der Grub-Shell kann man alle Befehle von Hand eingeben, die man sonst in der menu.lst konfiguriert.

Mit so einer Diskette und der Shell bekommt man jedes System recht schnell gebootet.

Unter Debian bemüht man ein grub-floppy, welches eine Bootdiskette automatisch erzeugt. Eine andere Möglichkeit ist, sich ein Diskettenimage aus dem Netz zu besorgen und es dann unter Linux mit `dd if=mein-grub-image of=/dev/fd0` auf die Diskette zu schreiben. Und selbst ein `cat stage1 stage2 > /dev/fd0H1440` tut es auch.

Interessant ist auch das Projekt http://www.ultimatebootcd.com/ - ein CD-Image auf dem viele Admin-Werkzeuge enthalten sind. Auch Grub ist dort zu finden.

Mit dieser Disk starten wir nun das System (oder aber direkt unter Linux durch Aufruf von grub). Es erscheint eine Kommandozeile, in der wir (mit englischer Tastaturbelegung, arg...) jetzt ein paar Kommandos einhacken:

 
01 root  (hdx,y)
02 setup (hdx)

In Zeile 01 wird die Partition angegeben, wo grub zu finden ist, also i.d.R. die Partition, wo auch der Kernel liegt, in obigen Beispiel (hd0,1). Danach wird ein setup ausgeführt auf der Platte, wo der Bootsektor hingeschrieben werden soll, typisch (hd0).

So, jetzt müsste alles laufen. Bootdiskette entfernen, System neu starten, ein Bootmenü sollte erscheinen und nach Auswahl sollte das System hochfahren.

2.4 Die Grub Kommandozeile

Bei der Installation des Boot-Sektors haben wir sie schon benutzt - die Grub Kommandozeile. Auf dieser kann man grundsätzlich fast alles eingeben, was auch automatisch über die menu.lst abgearbeitet wird.

Über eine Grub-Diskette und die dort vorhandene Kommandozeile ist es also möglich, jede beliebige installierte Linux-Distribution zu starten. Dies ist nützlich, wenn einem mal der Master-Boot-Record des installierten Linux verlorengegangen ist. Natürlich kann man darüber auch ein vorhandenes Windows-System starten. Oder BSD, DOS, memtest oder was auch immer.

Wichtig dabei ist, dass man die korrekten Einträge aus der menu.lst kennt. Nehmen wir einmal einen Menueintrag aus obigem Beispiel:

 
13 title           Hauptsystem mit Kernel 2.4.18
14 root            (hd0,1)
15 kernel          /boot/vmlinuz-2.4.18-586tsc root=/dev/hda2 ro hdc=ide-scsi
16 initrd          /boot/initrd.img-2.4.18-586tsc

Genau in dieser Art kann das auch auf der Kommandozeile eingegeben werden, wobei man title nicht braucht, das ist ja nur für's Menü von Interesse.

 
grub> root (hd0,1)
grub> kernel /boot/vmlinuz-2.4.18-586tsc root=/dev/hda2 ro hdc=ide-scsi
grub> initrd /boot/initrd.img-2.4.18-586tsc
grub> boot

Mit dem letzten boot startet das System mit den zuvor eingestellten Werten. Möchte man dagegen ein Windows System starten, welches auf hda0 installiert ist, schreibt man:

 
grub> root (hd0,0)
grub> chainloader +1
grub> boot

Bei der Eingabe wird einem aufgefallen sein, dass die englische Tastaturbelegung eingeschaltet ist. Wer eine deutsche Belegung braucht, kann sich eine angepasste Bootdiskette erstellen. Hierzu muss mittels des setkey-Befehls (in der menu.lst ablegen) die neue Belegung eingestellt werden.

3. Lust auf mehr?

Hier noch ein paar Anregungen, was es für weitere Möglichkeiten gibt:

  • Mit help bekommt man eine Auflistung aller Befehle in der grub Kommandozeile.
  • Mit password in der menu.lst kann ein Passwort angelegt werden, wobei man noch festlegen kann, welcher Eintrag im Menü nur mit Passwort gestartet werden kann (lock).
  • Mit setkey kann man die Tasten neu belegen. Wer also die englische Tastaturbelegung auf der grub-commandline hasst, der kann sich das in der menu.lst umschreiben.
  • Mit map drive kann man Festplatten vertauschen, was manchmal interessant ist, wenn Windows partout nur von der ersten Platte vernünftig starten will, man es aber auf die zweite ausgelagert hat.
  • Mit find file kann man in einem Dateisystem nach einer Datei suchen, wenn man mal vergessen hat, wie sein Kernel hieß oder wo die menu.lst zu finden ist.
  • Mit makeactive kann man eine Partition als aktive Partition markieren, was Windows zum Booten braucht.
  • Mit hide/unhide kann man Partitionen verstecken, was unter Windows mitunter Sinn macht.
  • Mit fallback kann ein alternatives System gebootet werden, wenn der Default-Eintrag nicht bootbar ist.
  • Mit cat file kann man sich Dateien anschauen. Sehr interessant, ohne eine Betriebssystem gebootet zu haben, kann man schon Dateien listen. Wer mehr will, als nur Dateien zu listen, sollte besser auf ein Disketten-Linux wie tomsrtbt setzen. Oder Knoppix. Sowas gehört ebenfalls in die Werkzeugkiste.
  • Wer nicht den Weg über stage1_5 gehen möchte, sollte alle stage1_5 files aus dem Verzeichnis /boot/grub löschen. Dann installiert setup automatisch nur stage1 und stage2.
  • Grub kann auch übers Netzwerk und TFTP ein Kernel laden

4. Debian-Spezifisches

Grub installiert man hier mit

 
apt-get install grub

Mit dem Kommando grub-install bringt man die Grub-Dateien dann auf die Festplatte (nach /boot/grub) und schreibt den Bootsektor.

 
grub-install /dev/hda

In diesem Beispiel befindet sich debian auf einer Partition hda2. Allerdings soll grub-install sich in den Bootsektor der ersten Festplatte schreiben, deshalb ist hier /dev/hda angegeben. Nach grub-install sollten unter /boot/grub die Grub-Dateien liegen. Eine menu.lst muss man sich nun in diesem Verzeichnis anlegen bzw. eine evtl. Vorhandene anpassen.

Bevor man neu bootet, sollte man mit einem hexdump /dev/hda -n 512 -C nachschauen, ob dort wirklich irgendwo das Wort GRUB auftaucht.

Auch sollte man sich vor einem Reboot sicherheitshalber aufschreiben, wie kernel und initrd heißen. Danach fertigt man sich mit grub-floppy /dev/fd0 noch eine Diskette für den Notfall an. Jetzt kann man das System neu starten und beten, dass alles funktioniert.

Wer sich jetzt irgendwie vertan hat, ist auch gut beraten, ein Notfall Linux auf CD zu haben, um ein paar Anpassungen auf seiner Debian-Platte machen zu können. Eine Möglichkeit wäre hier, Knoppix zu verwenden.

5. Tipps

5.1 Bootsektor sichern

Der Bootsektor ist eines der wichtigsten Sektoren auf der Festplatte, weil sich dort die Partitiontable befindet. Und auch der Bootloader. Gerne wird der Bootsektor durch andere Installationen überschrieben, z.B. wenn man Windows auf dem gleichen Rechner installiert, nachdem man Linux installiert hat.

Sinnvoll ist es deshalb, den Bootsektor zu sichern, um ihn später bei Bedarf wieder restaurieren zu können. Unter Linux geht das recht einfach mit dd:

 
# dd if=/dev/hda of=boot.bin bs=512 count=1

Dies sichert den ersten Sektor der Festplatte hda, welches ja der Bootsektor der ersten IDE-Platte ist (inkl. Partitionstabelle). Die Datei boot.bin wird dabei erzeugt und enthält den Bootsektor.

Ohne Partitionstabelle geht es so:

 
# dd if=/dev/hda of=boot.bin bs=446 count=1

Die ersten 446 Bytes sind das Bootprogramm, der Rest bis 512 ist Partitionstabelle, die hier nicht gesichert wird.

Ein Rückschreiben sollte man nur wohlüberlegt machen. Wenn man z.B. zwischendurch die Partitionierung der Platte geändert hat, darf man das bei der 512er Version nicht tun, sonst ist die neue Partitionierungstabelle verloren. Man sollte generell vor jedem zurückschreiben erstmal den aktuellen Bootsektor erneut sichern, um im Fehlerfall drauf zurückgreifen zu können.

 
# dd if=/dev/hda of=boot-current.bin bs=512 count=1
# dd if=boot-old.bin of=/dev/hda bs=512 count =1

Das erste Kommando sichert, das zweite schreibt den alten Boot-Sektor wieder auf die Platte.

Aus einer 512er Sicherung eines Bootsektors kann man sich übrigens sehr einfach auch eine 446er Version extrahieren, ebenfalls mit dd:

 
# dd if=boot.bin of=boot.bin.446 bs=446 count=1

5.2 Vor Windows Neuinstallation...

...sollte man den Bootsektor sichern, wie oben beschrieben. Nachdem Windows installiert ist, bootet man das vorhandene Linux von Hand. Hierzu muss man zuvor die Zeilen in der menu.lst notiert haben und von einer Grub-Bootdisk booten. Dann auf dem Prompt manuell die entsprechenden Zeilen aus der menu.lst eintippen, z.B:

 
grub> root (hd0,1)
grub> kernel /boot/vmlinuz-2.4.18-586 root=/dev/hda2 ro
grub> initrd /boot/initrd.img-2.4.18-586
grub> boot
 

Nach dem Boot schreibt man einfach den alten Bootsektor wieder auf Platte.

6. Referenzen

7. Stichwörter

grub, bootloader, lilo, booten

8. Fehler, Ergänzungen?

Habe ich etwas vergessen? Kann etwas verbessert werden? Haben Sie eine Idee? Ich freue mich über Feedback zu diesem Text.

9. Changelog

 
05.05.2007: Bootsektor sichern ohne Partitionstabelle. Danke Robin.
28.04.2005: Grub Kommandozeile hinzugefügt
25.04.2005: Tipps hinzugefügt
27.01.2005: Überarbeitung
15.11.2004: Umformatierung fürs Wiki
03.04.2004: Debian-Spezifisches.
27.10.2003: grub direkt von Linux starten.
17.10.2003: Kleine Syntaxbereinigung, GFDL.
09.09.2003: Erste Veröffentlichung. 

10. Copyright und Hinweise

Copyright (c) 2003 Winfried Mueller, www.reintechnisch.de

Es wird die Erlaubnis gegeben dieses Dokument zu kopieren, zu verteilen und/oder zu verändern unter den Bedingungen der GNU Free Documentation License, Version 1.1 oder einer späteren, von der Free Software Foundation veröffentlichten Version; mit keinen unveränderlichen Abschnitten, mit keinen Vorderseitentexten, und keinen Rückseitentexten.

Eine Kopie dieser Lizenz finden Sie unter GNU Free Documentation License.

Eine inoffizielle Übersetzung finden Sie unter GNU Free Documention License, deutsch.

In diesem Artikel werden evtl. eingetragene Warenzeichen, Handelsnamen und Gebrauchsnamen verwendet. Auch wenn diese nicht als solche gekennzeichnet sind, gelten die entsprechenden Schutzbestimmungen.

Alle Informationen in diesem Artikel wurden mit Sorgfalt erarbeitet. Trotzdem können Inhalte fehlerhaft oder unvollständig sein. Ich übernehme keinerlei Haftung für eventuelle Schäden oder sonstige Nachteile, die sich durch die Nutzung der hier dargebotenen Informationen ergeben.

Sollten Teile dieser Hinweise der geltenden Rechtslage nicht entsprechen, bleiben die übrigen Teile davon unberührt.