zurück zum Artikel

Das Dateisystem Ext3 tunen

| Dr. Oliver Diedrich

Ext3 ist das Standard-Dateisystem für Linux: robust, schnell, für alle Einsatzbereiche geeignet. Und trotzdem kann Ext3 zum Performance-Engpass werden. Selbst Fragmentierung ist bei Ext3 ein Thema.

Das Dateisystem Ext3 tunen

Dass sich Ext3 wie schon sein Vorgänger Ext2 als Standard-Dateisystem unter Linux etabliert hat, ist kein Wunder: Ext2/Ext3 bietet ordentliche Performance und ist dank jahrelangem, massenhaften Einsatz besser getestet als jedes andere Linux-Dateisystem. Zudem ist es so robust, dass sich selbst von einer defekten Festplatte, die teilweise nicht mehr lesbar ist, noch die meisten Daten retten lassen. Das unterscheidet Ext2/Ext3 von anderen Dateisystemen wie ReiserFS, deren filigrane Verwaltungsstrukturen auf der Platte schon durch wenige defekte Sektoren so sehr geschädigt werden können, dass alle Daten verloren sind.

Zu den robusten Metadatenstrukturen kommt das ausgereifte fsck-Tool: Selbst von beschädigten Dateisystemen kann e2fsck noch die meisten Daten retten – ein paar Tricks, mit denen man dem Tool bei besonders schwierigen Fällen auf die Sprünge helfen kann, verraten wir später in diesem Artikel. Wenn e2fsck aufgibt, ist das Dateisystem so kaputt, dass sich Daten höchstens noch mit einem Low-level-Datenrettungstool wie dd_rescue (beispielsweise auf der Knoppicillin-CD in c’t 26/07 enthalten) sektorweise auslesen und von Hand Dateien zuordnen lassen.

Wenn im Folgenden von Ext3 die Rede ist, meint das immer auch Ext2: Der hauptsächliche Unterschied ist das Journal von Ext3, das ein jederzeit konsistentes Dateisystem garantiert und die Zeit zum Überprüfen eines unsauber gemounteten Dateisystems von Stunden auf Sekunden reduziert. Ext2 und Ext3 sind vollständig kompatibel: Man kann ein Ext3-Dateisystem als Ext2 mounten, dann wird einfach sein Journal nicht benutzt, und ein Ext2-Dateisystem mit dem Befehl

tune2fs -j

nachträglich mit einem Journal versehen.

Wie jedes ordentliche Unix-Dateisystem arbeitet Ext3 im Wesentlichen mit drei Datenstrukturen: Verzeichnisse, Inodes und Datenblöcke. Verzeichnisse enthalten neben den Namen der Dateien nur noch die Nummern der ihnen zugeordneten Inodes. Dabei können durchaus mehrere Directory-Einträge auf einen Inode verweisen (harte Links). Auf der Platte sind Verzeichnisse als Dateien abgelegt, die sich lediglich durch den Dateityp (und ihren festgelegten Aufbau) von regulären Dateien unterscheiden.

Bild 1 [250 x 219 Pixel @ 16,5 KB]

Die wesentlichen Datenstrukturen von Ext2/Ext3: Verzeichnisse, Inodes und Datenblöcke

Inodes speichern mit Ausnahme des Namens alles, was man über eine Datei wissen muss: Größe, Dateityp (reguläre Datei, Verzeichnis, Gerätedatei, Pipe, Socket oder symbolischer Link), Besitzer, Anzahl der harten Links, Zugriffsrechte und -zeiten – und die Nummern der Datenblöcke, die die Daten enthalten. Diese Informationen (mit Ausnahme der Nummern der Datenblöcke) lassen sich mit dem Tool stat, viele davon auch mit ls erfragen. Die ls-Option -i gibt die Inode- Nummer einer Datei aus.

Inodes sind in Tabellen gespeichert, die mke2fs in reservierten Bereichen des Dateisystems anlegt. Bei symbolischen Links, deren Name nicht länger als 60 Zeichen ist, wird auch der Name direkt im Inode abgelegt – an Stelle der maximal 15 Blocknummern von jeweils 4 Byte (so genannte schnelle symbolische Links). Ansonsten ist der Name der Datei, auf die der Symlink verweist, in der Datei gespeichert, die der Inode repräsentiert.

Datenblöcke schließlich speichern die eigentlichen Daten. Sie fassen mehrere Sektoren von 512 Byte (die kleinste adressierbare Einheit auf Festplatten) zusammen. Ext3 arbeitet mit Blockgrößen von 1024, 2048 oder 4096 Byte – welche bei einem Dateisystem zum Einsatz kommt, legt der Ext3-Formatierer mke2fs beim Anlegen des Dateisystems fest. Theoretisch unterstützt Ext3 Blockgrößen bis zu 64 KByte, auf der x86- und x64-Architektur ist bei 4 KByte allerdings Schluss: Dann sind die Blöcke im Dateisystem gerade so groß wie die Speicherseiten des Kernels im RAM, was dem Betriebssystem das Paging erleichtert.

Große Blöcke vereinfachen das Verwalten des Datenbestandes und erlauben größere Dateisysteme: Ext3 verwendet 32-Bit- Werte zum Durchnummerieren der Blöcke, kann also nur gut vier Milliarden Blöcke – 4 TByte bei einer Blockgröße von 1024 Byte, 16 TByte bei 4096 Byte – adressieren. Zudem belegen die Verwaltungsstrukturen des Dateisystems bei kleinen Blockgrößen einen größeren Anteil des Platzes auf der Festplatte.

Andererseits können große Blöcke eine Menge Plattenplatz verschwenden, da Dateien immer ganze Blöcke belegen, auch wenn sie nur wenige Byte enthalten: Im Schnitt verplempert jede Datei einen halben Block – je größer die Blöcke und je kleiner die Dateien, desto mehr fällt diese so genannte interne Fragmentierung ins Gewicht. Ext3 enthält zwar schon Datenstrukturen zur Verwaltung mehrerer Fragmente in einem Datenblock (Fragmente meint in diesem Zusammenhang Dateireste, die keine ganzen Blöcke belegen); das Feature ist allerdings nicht implementiert, auch wenn mke2fs dafür bereits den Parameter -f vorsieht.

Beim Formatieren wählt mke2fs die Blockgröße entsprechend der Größe des Dateisystems: 1 KByte bei bis zu 512 MByte, ansonsten 4 KByte. Mit der mke2fs-Option -b lässt sich die Blockgröße aber auch von Hand festlegen – das kann sinnvoll sein, wenn auf einem Dateisystem fast nur sehr kleine Dateien gespeichert werden sollen und es einem um den bei größeren Blöcken verschwendeten Platz leidtut.

Der ganze Aufbau von Ext3 ist auf das optimiert, was Anwendungen normalerweise tun: Daten aus einer Datei mit einem bestimmten Namen lesen oder in eine solche Datei hineinschreiben. Für das Dateisystem heißt das, es muss sehr schnell die Daten finden, die zu einem Dateinamen gehören. Dreh- und Angelpunkt dabei sind die über den Directory-Eintrag erreichbaren Inodes mit Metadaten und Verweisen auf die Datenblöcke. Der Weg rückwärts ist nicht möglich: Um herauszufinden, zu welcher Datei ein bestimmter Datenblock gehört, müssen zunächst alle Inodes auf die angefragte Blocknummer und dann die Verzeichnisse nach dieser Inode-Nummer durchsucht werden. Das Low-level-Werkzeug debugfs erledigt das mit den Befehlen icheck und ncheck (siehe Textkasten Dateisystem-Debugger [1]).

Der Zugriff auf die Inodes muss daher besonders effizient erfolgen. Das wird unter anderem dadurch sichergestellt, dass die Inodes bereits bei der Formatierung als statische Tabellen auf die Platte geschrieben werden. Eine Konsequenz davon ist, dass sich die Zahl der Inodes nach dem Anlegen des Dateisystems nicht mehr verändern lässt. Da jeder Datei ein eindeutiger Inode zugeordnet sein muss, kann es nicht mehr Dateien als Inodes geben. mke2fs legt standardmäßig einen Inode pro 4 KByte bei Dateisystemen bis 512 MByte an, ansonsten einen Inode pro 8 KByte.

Wer glaubt, dass er es besser weiß als mke2fs, weil er nur wenige große oder sehr viele kleine Dateien speichern will, kann beim Aufruf von mke2fs mit der Option -i einen beliebigen Wert vorgeben, für wie viele Byte Daten jeweils ein Inode benötigt wird. Bei wenigen Inodes kann man weniger Dateien anlegen, schlägt aber einige Megabyte mehr nutzbaren Plattenplatz heraus – immerhin belegt jeder Inode standardmäßig 128 Byte auf der Platte. Viele Inodes erlauben das Anlegen von mehr Dateien.

Über die mke2fs-Option -T Typ kann man auch auf einige in /etc/mke2fs.conf vordefinierte Einstellungen zugreifen: small (Default bei Dateisystemen bis 512 MByte) wählt eine Blockgröße von 1 KByte bei einem Inode pro vier Blöcken, news eine Blockgröße von 4 KByte mit einem Inode pro Block, largefile und largefile4 stehen für einen Inode pro 256 und 1024 Blöcken bei einer Blockgröße von 4 KByte.

Auch die feste Größe der Inodes von 128 Byte erlaubt einen schnellen Zugriff auf diese zentrale Datenstruktur. Mit

mke2fs -I Inode-Größe

kann man beim Anlegen des Dateisystems einen größeren Wert vorgeben, wobei der Wert glatt durch 128 teilbar sein muss. In größeren Inodes kann Ext3 erweiterte Attribute ablegen (siehe c’t 23/03, S. 218).

Aber wie kriegt man in einer statischen Datenstruktur von 128 Byte die Nummern der Millionen Datenblöcke unter, die man für gigabytegroße Dateien braucht? Gar nicht – ein Ext3-Inode speichert genau 15 Blocknummern. Die ersten zwölf verweisen direkt auf Datenblöcke, Block 13 auf einen Datenblock mit Blocknummern (indirekt adressierte Blöcke), Block 14 auf einen Block, der auf Blöcke mit Blocknummern verweist (doppelt indirekt), und Block 15 verweist auf dreifach indirekt adressierte Blöcke. Damit kann ein Inode bei 4 KByte Blockgröße (also 1024 Blocknummern à 4 Byte in einem indirekten Block) 12 + 1024 + 10242 + 10243, also gut eine Milliarde Blocknummern verwalten.

Bild 2 [210 x 231 Pixel @ 17,5 KB]

Über die indirekte Adressierung lassen sich mit 15 Blocknummern mehrere TByte adressieren.

Die daraus resultierende maximale Dateigröße von etwas über 4 TByte ist allerdings ein theoretischer Wert, da die Inodes die Anzahl der zu einer Datei gehörenden Festplattensektoren 512 Byte speichern – stat und der stat-Befehl in debugfs geben diesen Wert als Blockcount aus. Da es sich dabei um einen 32-Bit-Wert handelt, liegt die maximale Dateigröße von Ext3 bei 2 TByte.

Die 2-GByte-Grenze, mit der sich manche alten Anwendungen bei der Dateigröße herumschlagen, ist übrigens nicht in Ext3 begründet, sondern in den System-Calls zum Zugriff auf Dateien. Die Funktionen und Datenstrukturen zum Zugriff auf Dateien arbeiten traditionell mit einem vorzeichenbehafteten 32-bittigen File Offset (ein Zeiger, der jedes beliebige Byte innerhalb einer Datei adressieren kann), was lediglich eine Dateigröße von 231-1 Byte zulässt. Diese Grenze ist aber längst gefallen: Large File Support (LFS) heißt das Zauberwort (s. c't 10/00, S. 256).

Neben den Directories, den Inode-Tabellen und den Datenblöcken selbst arbeitet Ext3 noch mit einigen weiteren Datenstrukturen. Zwei Bitmaps führen Buch darüber, welche Datenblöcke und Inodes belegt und welche frei sind. Um auch hier für Effizienz beim Zugriff zu sorgen, organisiert Ext3 das Dateisystem in Blockgruppen – eine Art Partitionierung innerhalb des Dateisystems.

Beim Lesen, Schreiben und Anlegen von Dateien hüpfen die Festplattenköpfe nämlich munter zwischen Datenblöcken, Verzeichnissen, Inode-Tabelle sowie Inode- und Block-Bitmap hin und her. Nun brauchen Bewegungen der Plattenköpfe aber im Schnitt mehrere Millisekunden – in dieser Zeit kann man von einer schnellen Platte auch ein Megabyte an Daten lesen. Unter der Annahme, dass Kopfbewegungen umso länger dauern, je weiter die entsprechenden Sektoren auf der Platte auseinanderliegen, ist es vorteilhaft, zusammengehörende Teile der Verwaltungsstrukturen und Daten nahe beieinander zu speichern.

Das macht Ext3 mit den Blockgruppen: Jede Blockgruppe enthält (neben einem Block Group Descriptor mit einigen statistischen Angaben, wie sehr diese Blockgruppe bereits genutzt ist) einen Teil der Inode-Tabelle sowie den Teil der Inode- und Block-Bitmap, der diesen Ausschnitt der Inode-Tabelle und die Datenblöcke der Blockgruppe abbildet. Ext3 bemüht sich, Dateien so anzulegen, dass Verwaltungsstrukturen – Inode und Verzeichnis – und Datenblöcke innerhalb einer Blockgruppe liegen.

Die Anzahl der angelegten Blockgruppen ist abhängig von der Größe des Dateisystems. Der Parameter lässt sich zwar mit der mke2fs-Option -g setzen, die Ext3-Entwickler raten allerdings davon ab, da mke2fs bereits den optimalen Wert wählt. Da für die Block-Bitmap der Blockgruppe bloß ein Datenblock vorgesehen ist, kann eine Blockgruppe bei einer Blockgröße von 4 KByte maximal 32 768 Blöcke, also 128 MByte enthalten.

Die letzte Ext3-Datenstruktur ist der Superblock, der das Dateisystem selbst beschreibt. Er enthält alle Informationen, die zur korrekten Interpretation der Daten im Dateisystem nötig sind: Blockgröße, Anzahl der Blöcke und Inodes, Blockgruppen, Inode-Größe, erster gültiger Inode (einige Inodes sind für interne Zwecke reserviert, beispielsweise der Inode 2 für das Root-Verzeichnis und der Inode 8 bei Ext3 für das Journal). Alle diese Informationen (und noch einige mehr) geben das debugfs-Kommando stats und der Befehl dumpe2fs -h aus.

Die als reserviert bezeichneten Blöcke (standardmäßig fünf Prozent aller Blöcke, änderbar über den mke2fs-Parameter -m) darf nur root belegen; das soll sicherstellen, dass dem System noch ein wenig Luft zum Leben bleibt, falls ein User das Dateisystem komplett volllaufen lässt.

Außerdem speichert der Superblock Statusinformationen, etwa, wie viele Inodes und Blöcke noch frei sind, wann das Dateisystem zuletzt gemountet und wann es zuletzt von e2fsck überpüft wurde, sowie den aktuellen Zustand (clean, wenn alles in Ordnung ist). Hier finden sich auch die ungeliebten Einstellungen, die dafür sorgen, dass immer dann ein Dateisystemcheck läuft, wenn man „mal eben schnell“ booten muss. Ext3 speichert sowohl eine maximale Anzahl an Mount-Vorgängen als auch einen maximalen zeitlichen Abstand seit dem letzten e2fsck-Lauf. Wenn einer dieser Werte erreicht ist, wird zwangsüberprüft. Bei Rechnern, die häufig gebootet werden, kann man diese Werte getrost hochsetzen:

tune2fs -c 100 -i 180

erlaubt 100 Mount-Vorgänge oder ein halbes Jahr zwischen zwei e2fsck-Läufen. Werte von 0 schalten den automatischen Test komplett ab. Der überall zu lesende Rat, das Feature auf keinen Fall abzuschalten, ist zwar nicht grundsätzlich falsch, allerdings längst nicht mehr so wichtig: Plattendefekte findet man schneller mit den smartmontools, die das Befinden der Platte direkt abfragen; die Zeiten von Kernel-Bugs, die das Dateisystem korrumpieren, sind vorbei; und Probleme im Speicher oder Chipsatz dürfte man an anderer Stelle schneller merken als an beschädigten Ext3-Datenstrukturen.

Da der Superblock für den Zugriff auf das Dateisystem unverzichtbar ist, schreibt ihn mke2fs sicherheitshalber mehrfach auf die Platte. Sollte e2fsck bei einem beschädigten Dateisystem wegen eines kaputten Superblocks die Arbeit verweigern, kann man das Tool anweisen, einen der Reserve-Superblocks zu verwenden:

e2fsck -b Superblock

mke2fs gibt die Blocknummern der alternativen Superblocks nach dem Anlegen des Dateisystems aus, aber wer hat die im Fall der Fälle schon notiert? Hier hilft eine nützliche Option von mke2fs weiter:

mke2fs -n Device

tut lediglich so, als würde es das Dateisystem neu anlegen, gibt aber alle seine Parameter – darunter auch die Position der Superblocks – aus. Das funktioniert natürlich nur, wenn man mke2fs mit denselben Optionen aufruft wie damals, als das Dateisystem angelegt wurde. Aber im Normalfall muss man dem Formatierer ja überhaupt keine Optionen mitgeben – mke2fs wählt selbst Werte, die für die meisten Anwendungsfälle passen.

Für den Super-GAU hält mke2fs noch die Option -S bereit – damit werden lediglich die Superblöcke und die Blockgruppendeskriptoren neu geschrieben, während Directories, Inode- und Bitmap-Tabellen erhalten bleiben. Danach ist ein e2fsck-Lauf erforderlich. Im Idealfall findet man anschließend alle Dateien wieder, eine Garantie gibt es jedoch nicht – schlimmstenfalls sind trotzdem alle Daten verloren. Natürlich muss auch mke2fs -S mit exakt den gleichen Optionen aufgerufen werden wie beim Anlegen des Dateisystems.

Der Superblock kann noch mehr Informationen speichern: beispielsweise Mount-Optionen, die immer beim Einbinden des Dateisystems gelten sollen, auch wenn sie nicht explizit angegeben werden. Mit

tune2fs -o acl

beispielsweise sorgt man dafür, dass das Dateisystem immer mit Unterstützung für Access Control Lists (ACLs) gemountet wird (siehe c't 23/03, S. 218) Auch der Name des Dateisystems (volume name), den manche Distributionen in /etc/fstab verwenden, ist im Superblock gespeichert und lässt sich über den Parameter -L nachträglich mit tune2fs oder bereits beim Anlegen des Dateisystems mit mke2fs setzen.

Die „Filesystem features“ legen diverse Eigenschaften des Dateisystems fest – ob es auf Fehler überprüft werden muss (needs_recovery, das Flag wird beim Mounten gesetzt und beim Aushängen gelöscht, sodass es anzeigt, ob das Dateisystem sauber ausgehängt wurde); ob sich das Dateisystem noch mit resize2fs weiter vergrößern lässt (resize_inode); ob es Dateien über 2 GByte Größe unterstützt (large_file); ob nur eine begrenzte Anzahl an Reserve-Superlocks angelegt wurde (sparse_super); ob der Verzeichniseintrag Informationen über die Art der Datei enthalten soll (filetype); ob Verzeichniseinträge in Baumstrukturen gespeichert werden sollen (dir_index). Einige dieser Eigenschaften lassen sich über tune2fs -O verändern (ein vorangestelltes ^ schaltet ein gesetztes Feature ab); dem sollte sicherheitshalber ein mit der Option -f erzwungener e2fsck-Lauf folgen. Am besten legt man diese Features aber bereits über mke2fs -O beim Anlegen des Dateisystems fest.

Welche Fatures mke2fs standardmäßig setzt, liest das Programm aus der Datei /etc/mke2fs.conf. Bei den meisten Distributionen sind sparse_super, filetype, resize_inode und dir_index vorgegeben. Sofern das Betriebssystem große Dateien unterstützt, setzt mke2fs auch das Feature large_file.

dir_index (gelegentlich auch als htree bezeichnet) ist ein für die Performance besonders wichtiges Dateisystem-Feature. Ursprünglich speicherte Ext2 die Dateinamen eines Verzeichnisses als lange verkettete Liste. Das ist eine schön einfache Datenstruktur, die aber den Nachteil hat, dass Operationen darin mit einer wachsenden Zahl von Einträgen immer länger dauern. In einem Dateisystemvergleich vor fünf Jahren hatten wir gefunden, dass die Performance von Ext3 schon bei einigen tausend Dateien in einem Verzeichnis drastisch einbricht (siehe c't 6/02, S. 228).

Bild 3 [250 x 185 Pixel @ 18,7 KB]

Abhängig vom Hauptspeicher bricht die Performance von Ext3 in sehr gut gefüllten Directories ein. Gemessen wurde das Auflisten eines Verzeichnisses mit readdir() im Single User Mode.

Inzwischen verwaltet Ext3 ähnlich wie die Dateisystemkonkurrenz ReiserFS, XFS und JFS Verzeichnisse in Baumstrukturen, wenn dir_index gesetzt ist, was Directory-Operationen drastisch beschleunigt. Einbrüche in der Performance treten erst bei sehr gut gefüllten Verzeichnissen mit zigtausenden Dateien auf. Dabei dürfte es sich allerdings in erster Linie um einen Caching-Effekt handeln: Je weniger Hauptspeicher, desto früher erfolgt der Einbruch. Allerdings scheint der Linux-Kernel nicht unbegrenzt Speicher zum Cachen von Directory-Strukturen zu verwenden: Im Single User Mode fanden wir bereits beim Sprung von 1 auf 2 GByte RAM keine Leistungssteigerung mehr. Im normalen Betrieb mit einer Reihe weiterer Prozesse sahen wir zwischen 1 und 2 GByte allerdings noch einen Unterschied.

Die Option dir_index lässt sich mit dem Befehl

tune2fs -O dir_index

auch nachträglich aktivieren, wirkt sich dann allerdings nur auf neu angelegte Verzeichnisse aus. Die bereits bestehenden Verzeichnisse eines nicht gemounteten Dateisystems stellt der Befehl

e2fsck -fD

auf dir_index um, nachdem das Feature dir_index gesetzt wurde. Danach sollte man sicherheitshalber einen weiteren e2fsck-Lauf mit der Option -f erzwingen.

Diese Option ist auch nützlich, wenn in Verzeichnissen ständig Dateien angelegt und gelöscht werden: Da Ext3 die Namen gelöschter Dateien nicht aus den Verzeichnisdateien entfernt, wachsen die immer weiter an – auch wenn die meisten Einträge gar nicht mehr verwendet werden. e2fsck -fD entfernt ungültige Einträge aus den Verzeichnisdateien und baut die Baumstruktur der Dateinamen neu auf, was die Geschwindigkeit bei Verzeichnisoperationen beträchtlich erhöhen kann.

Der wesentliche Unterschied zwischen Ext2 und Ext3 ist das bereits erwähnte Journal. Die Idee dahinter ist simpel: Eine Änderung im Dateisystem, etwa das Anlegen einer neuen Datei, wirkt sich an vielen Stellen aus – es werden ein neuer Verzeichniseintrag und ein neuer Inode angelegt, Datenblöcke und Inode in der Block- und Inode-Bitmap als reserviert markiert, im Inode des Verzeichnisses ändert sich die letzte Zugriffszeit, die Dateisystemstatistiken im Superblock werden aktualisiert und die Daten selbst geschrieben. Fällt zwischen diesen diversen Schreibzugriffen in unterschiedlichen Datenstrukturen der Strom aus oder stürzt das System ab, ist das Dateisystem inkonsistent – es existiert etwa ein Inode ohne den zugehörigen Verzeichniseintrag (Stichwort lost+found).

Um das verhindern, schreibt Ext3 die Änderungen zunächst in sein Journal. Bis zu dem Zeitpunkt, an dem alle zusammengehörenden Änderungen (eine Transaktion) im Journal stehen, sind die (alten) Metadaten im Dateisystem konsistent. Ist die Transaktion vollständig, sind die (neuen) Metadaten im Journal konsistent und können bei passender Gelegenheit ins Dateisystem überspielt werden. Im Falle eines Crashs muss e2fsck lediglich die vollständigen Transaktionen im Journal erneut ins Dateisystem spielen, um die Konsistenz herzustellen. Unvollständige Transaktionen im Journal bleiben unberücksichtigt, hier sind ja noch die (alten) Daten im Dateisystem gültig.

Ext3 kennt verschiedene Betriebsmodi des Journals, die sich beim Mounten über

mount -o data=MODUS

auswählen lassen. Der Default-Modus ist ordered: Hier schreibt Ext3 zunächst die Daten auf die Platte, bevor die geänderten Metadaten im Journal landen. Das garantiert lediglich die Konsistenz der Metadaten: Stürzt der Rechner ab oder fällt der Strom aus, bevor die Transaktion im Journal abgeschlossen ist, sind die bereits geschriebenen Daten verloren, da neu belegte Blöcke noch nicht ihrem Inode zugeordnet und in der Block-Bitmap als belegt markiert sind.

Mit data=journal wandern auch die Daten selbst durch das Journal, was die Performance des Dateisystems drastisch senkt – schließlich müssen alle Daten zweimal auf die Platte geschrieben werden. Im Modus writeback können die Metadaten im Journal vor den Daten geschrieben werden. Das kann die Performance etwas erhören, da Ext3 so die Schreibzugriffe besser optimieren kann; allerdings können im Falle eines Crashs in scheinbar neu angelegten Dateien alte Daten auftauchen und vermeintlich korrekt angelegte Dateien leer sein.

Um die Performance zu beschleunigen, ist es möglich, das Journal auf einer anderen Platte abzulegen als das Dateisystem – so sind gleichzeitige Zugriffe im Dateisystem und im Journal möglich. Dazu ist zunächst mit

mke2fs -O journal_dev DEVICE

das externe Journal anzulegen und anschließend

mke2fs -J device=DEVICE

aufzurufen.

Es gibt einen weiteren Unterschied zwischen Ext2 und Ext3: Die beiden Dateisysteme verhalten sich beim Löschen von Dateien unterschiedlich. Während Ext2 lediglich im Inode die Deletion Time setzt (und Datenblöcke und Inode in der Block- und Inode-Bitmap als frei markiert), löscht Ext3 zusätzlich die Blocknummern im Inode. Das macht es leichter, das Dateisystem nach einem Crash in einen konsistenten Zustand zu bringen, führt aber auch dazu, dass Programme zum Wiederherstellen gelöschter Dateien auf Ext2 (der Befehl lsdel in debugfs und spezielle Undelete-Tools [2]) mit Ext3 nicht mehr funktionieren.

Stellt man unter Linuxern die Frage nach einem Defragmentier-Tool für Linux, hat das einen ähnlichen Effekt wie der Ruf „Jehova“ in Monty Pythons „Das Leben des Brian“: „Braucht man nicht“, „Ext3 fragmentiert nicht“, „so etwas gibt es nur bei Windows“ – das etwa ist, was man in Newsgroups und Hilfeforen zu lesen kriegt.

Wenn man sich mit Ext3 und Fragmentierung beschäftigt, gilt es zunächst zwischen zwei sehr verschiedenen Dingen zu unterscheiden: der bereits erwähnten internen und der externen Fragmentierung. Letzteres bezeichnet Dateien, deren Datenblöcke nicht hintereinander liegen, sondern über die Platte verstreut sind, was beim sequenziellen Lesen und Schreiben der Daten mehr zeitraubende Kopfbewegungen erfordert als nötig. Interne Fragmentierung kostet „nur“ Speicherplatz, externe Fragmentierung hingegen Performance.

Bild 4 [250 x 179 Pixel @ 46,8 KB]

Auf dem IMAP-Server, der jede Mail in einer eigenen Datei ablegt, sind die freien Bereiche in vielen Blockgruppen stark fragmentiert.

Ext3 ist ziemlich gut darin, externe Fragmentierung zu verhindern und die Kopfbewegungen zu minimieren – so versucht das Dateisystem beispielsweise, für neu angelegte Dateien einen Bereich von acht freien Blöcken vorzubelegen. Zudem sorgt der Schreib-Cache dafür, dass Daten, die eine Anwendung etappenweise wegschreibt, in einem Rutsch und damit häufig auch am Stück auf die Platte geschrieben werden. Bei Dateien, die langsam wachsen – etwa Verzeichnisse, die sich nach und nach füllen –, hilft das freilich nicht; und auch, wenn immer wieder Dateien unterschiedlicher Größe angelegt und gelöscht werden und so zwangsweise kleine Belegungslöcher mit freien Blöcken entstehen, lässt sich Fragmentierung nicht vermeiden.

Beim Kampf gegen die Fragmentierung kommt dem Dateisystem zudem eine andere Optimierungsstrategie in die Quere: die Lokalität von Daten und Metadaten, die über die Blockgruppen erreicht werden soll. Da Ext3 die Dateien in einem Verzeichnis bevorzugt in der gleichen Blockgruppe ablegt, kann es zu Fragmentierung kommen, obwohl eigentlich noch viel zusammenhängender freier Platz auf der Platte ist. Die gelegentlich zu lesende Behauptung, Ext3 fragmentiere erst, wenn das Dateisystem zu 80 oder 90 Prozent gefüllt ist, stimmt daher nur bedingt: Je nach Nutzung kann es auch auf einem Dateisystem mit reichlich freiem Platz zu Fragmentierung kommen.

So fanden wir auf einem intensiv genutzten IMAP-Server, der alle Mails als eigene Dateien ablegt, in vielen Blockgruppen eine heftige Zersplitterung der freien Bereiche – obwohl von insgesamt 1,4 TByte Plattenplatz noch über 900 GByte frei waren.

Der Fragmentierungsgrad lässt sich mit mit dem Tool dumpe2fs überprüfen, das – ohne Optionen aufgerufen – für jede Blockgruppe die Fragmentierung der freien Bereiche ausgibt. Idealerweise sollte sich in jeder Blockgruppe ein großer zusammenhängender Bereich mit freien Blöcken befinden; je mehr kleine Bereiche mit freien Blöcken, desto höher das Risiko, dass neu angelegte Dateien in dieser Gegend auf mehrere Fragmente verteilt werden. Im schlimmsten Fall sind die freien Blöcke einzeln über die Blockgruppe verteilt. Dumpe2fs kann ohne Risiko auch über gemountete Dateisysteme laufen, allerdings kann es dabei zu Inkonsistenzen kommen – beispielsweise können die zusammenfassenden Statistiken, die dumpe2fs zunächst für jede Blockgruppe ausgibt, weniger oder mehr freie Blöcke anzeigen, als danach einzeln aufgelistet werden.

Da der Output von dumpe2fs bei Hunderten oder Tausenden von Blockgruppen sehr unübersichtlich wird, haben wir das Perl-Skript eval_dumpe2fs [3] geschrieben, das die Ausgabe von dumpe2fs einliest und eine zusammenfassende Statistik ausgibt. Das Skript erwartet, dass dumpe2fs seine Meldungen auf Englisch ausgibt; daher muss auf lokalisierten Linux-Systemen die Variable $LANG gesetzt werden:

LANG=C dumpe2fs DEVICE | eval_dumpe2fs

Das Skript gibt für jede Blockgruppe aus, auf wie viele Bereiche mit mindestens zwei Blöcken („chunks“) und einzelne Blöcke die freien Blöcke verteilt sind und wie groß ein freier Bereich im Durchschnitt ist („avg. chunk size“). Kritische Werte werden dabei fett gedruckt. Eine Statistik am Ende fasst zusammen, wie viele Blockgruppen wie stark zersplittert sind.

Dumpe2fs zeigt auch an, wie viele Inodes in jeder Blockgruppe noch frei sind – bei sehr vielen kleinen Dateien können die Inodes einer Blockgruppe aufgebraucht sein, bevor alle Blöcke belegt sind. Das schadet der Performance zwar erst einmal nicht, aber wenn die Inodes in zu vielen Blockgruppen ausgehen, kann Ext3 die Lokalität von Daten und Metadaten nicht mehr gewährleisten. Und wenn alle Inodes in einem Dateisystem aufgebraucht sind, können dort keine Dateien mehr gespeichert werden – egal, wie viele freie Blöcke es noch gibt.

Natürlich will man nicht nur wissen, wie sehr die freien Bereiche zersplittert sind – das lässt ja lediglich eine Abschätzung zu, wie gut Ext3 bei neu angelegten Dateien eine Fragmentierung verhindern kann. Mindestens genauso interessant ist der Fragmentierungsgrad des bestehenden Dateisystems.

E2fsck gibt nach einem Test des Dateisystems aus, wie hoch der Prozentsatz an nicht zusammenhängend gespeicherten („non-contiguous“) Dateien ist (bei sauber ausgehängten Dateisystemen muss man den Test mit -f erzwingen). Dieser Wert fällt selbst bei recht gut gefüllten Dateisystemen überraschend niedrig im einstelligen Prozentbereich aus. Der Grund: e2fsck zählt dabei auch Dateien mit, die gar keine Datenblöcke enthalten (leere Dateien, schnelle symbolische Links, Gerätedateien). Außerdem sind auf typischen Linux-Systemen die meisten Dateien kleiner als 4 KByte, belegen also (bei einer Blockgröße von 4 KByte) nur einen Block; und da kann natürlich nichts fragmentieren. Der Anteil der fragmentierten Dateien liegt daher auch bei maximaler Fragmentierung nie höher als der Anteil der Dateien mit einer Größe von mehr als einem Block an allen Dateien.

Unser Tool ext2_frag [4] geht etwas anders an die Sache heran und zählt leere sowie Gerätedateien gar nicht erst mit. Es teilt die Dateien in verschiedene Klassen je nach der Zahl der belegten Blöcke ein. Außerdem gibt es in den zusammenfassenden Statistiken den Prozentsatz der fragmentierten Dateien an den Files aus, die mehr als einen Block belegen – dieser Wert liegt typischerweise um den Faktor zwei bis drei höher als der auf alle Dateien bezogene, wie ihn e2fsck ausgibt.

Das bloße Zählen der fragmentierten Dateien hat allerdings den Nachteil, dass es nicht berücksichtigt, wie stark eine Datei fragmentiert ist – es unterscheidet ja nur zwischen fragmentiert und unfragmentiert. Ext2_frag berechnet daher für jede Größenklasse die durchschnittliche Zahl der Fragmente pro Datei.

Ein aussagekräftigeres Maß für die gesamte Fragmentierung ist die Zahl der nicht aufeinanderfolgenden Blöcke, ins Verhältnis gesetzt zur Zahl der möglichen Sprünge (die Zahl der belegten Datenblöcke minus eins, summiert über alle Dateien). Ext2_frag nennt das „Fragmentation index“. Dieser Wert ist 0, wenn alle Dateien am Stück gespeichert sind, und 100 bei maximaler Fragmentierung.

Bild 6 [250 x 153 Pixel @ 23,3 KB]

Bei einem Arbeitsplatzrechner hält sich die Fragmentierung in Grenzen.

Das E2fsprogs-Paket bringt außerdem das Tool filefrag mit, das ausgibt, aus wie vielen Fragmenten („extents“) eine Datei besteht. Das Tool gibt auch an, aus wie vielen Fragmenten die Datei mindestens bestehen muss: Dateien über 128 MByte Größe passen nicht mehr in eine Blockgruppe und müssen daher zwangsläufig aus mehreren Fragmenten bestehen.

Etwas komfortabler als filefrag ist das Programm fragments [5], das ganze Verzeichnisse durchsucht (mit der Option -r auch rekursiv) und neben zusammenfassenden Statistiken für jedes Verzeichnis (Option -d) auch Details zur Fragmentierung der einzelnen Dateien ausgeben kann (Option -f).

Während ext2_frag low level auf das Dateisystem zugreift und den Inhalt jedes Inodes analysiert, um die belegten Blöcke zu ermitteln, verwenden filefrag und fragments einen speziellen Ioctl, der die von einer Datei belegten Datenblöcke zurückliefert. Dieser Ioctl funktioniert allerdings nur mit regulären Dateien, nicht mit Verzeichnissen. Die Tools können daher beim gleichen Dateisystem zu etwas unterschiedlichen Ergebnissen kommen. Dateils dazu finden Sie im Textkasten Fragmentierung messen [6].

Fragmentierung kostet I/O-Performance, da sequenzielle Lese- und Schreiboperationen durch die Kopfbewegungen unnötig gebremst werden. In vielen Anwendungskontexten spielt das allerdings keine große Rolle: Meist greifen mehrere Anwendungen parallel auf die Platte zu, sodass gleichzeitig verschiedene Dateien angepackt werden müssen. Linux minimiert dabei die Kopfbewegungen durch ein Umsortieren der Schreib- und Lesezugriffe. Zum anderen werden die meisten Dateizugriffe sowieso aus dem Cache bedient: Dank dateiweisem Readahead sind die Daten häufig schon eingelesen, wenn sie eine Anwendung anfordert.

Wenn die I/O-Last allerdings sowieso sehr hoch ist, etwa weil permanent sehr viele Daten gelesen und geschrieben werden müssen, kann sich eine Fragmentierung durchaus bemerkbar machen – wir haben das bei unserem Cyrus-IMAP-Server erlebt, der ein gewaltiges Mail-Aufkommen wegschaffen muss und dabei jede Mail in einer eigenen Datei ablegt. Hier waren (trotz reichlich freiem Platz auf der Platte) ein Viertel aller Dateien mit einer Größe über einem Block fragmentiert – selbst unter den kleinen Dateien bis 48 KByte, die nur direkte Blöcke belegen (siehe Tabelle unten). Hier kann eine Defragmentierung durchaus helfen.

182370304 inodes (172828519 free)
364735735 blocks of 4096 bytes (248336353 free)
number of blocksfilesfragmentedpercentfragments/file
1572230300.001.00
<= 12349271476137221.801.37
<= 52451312615496430.209.30
<= 103626247923335.1864.78
<= 410821673967044.62148.23
> 41082462151861.66380.00
all files 97785259367579.582.16
files > 1 block 405622293675723.093.80
Fragmentation index: 8.80 percent.

Auch bei viel freiem Platz – das Dateisystem ist nur zu einem Drittel belegt – kann Ext3 mächtig fragmentieren.

Allerdings gibt es keinen Defragmentierer für Ext3 – das vor Urzeiten geschriebene ext2_defrag wird schon lange nicht mehr weiterentwickelt und kann mit aktuellen Ext2- und Ext3-Versionen nicht umgehen. Die einzige Methode zur Defragmentierung besteht darin, alle Dateien auf ein frisch angelegtes Dateisystem umzukopieren oder sie in ein tar-Archiv zu packen (am besten ebenfalls auf einem anderen Dateisystem), sie zu löschen und das Archiv danach wieder auszupacken. Dabei werden, angenehmer Nebeneffekt, auch die Verzeichnisdateien neu angelegt, sodass sie nur noch die Dateien enthalten, die tatsächlich in dem Verzeichnis gespeichert sind.

Mit dem bereits erwähnten Tool fragments können Sie auch herausfinden, ob vor allem einzelne Verzeichnisse (etwa solche, in denen eine Anwendung sehr häufig Dateien anlegt und löscht) fragmentiert sind und das Umkopieren auf diese Verzeichnisse beschränken. Bei unserem IMAP-Server hat das Umkopieren durchaus etwas gebracht: Die I/O-Spitzen in Zeiten besonders intensiven Mailaufkommens sind danach deutlich gesunken. (odi [7])

Dateisystem-Debugger Das Debugfs-Programm aus dem E2fsprogs-Paket ist ein wundervolles Tool, um die Feinheiten von Ext3 zu erforschen. So gibt der Befehl [VERBATIM11] oder [VERBATIM12] alle Informationen aus dem Inode aus – inklusive der Datenblöcke, die die Datei belegt. mi erlaubt das Modifizieren des Inode-Inhalts – manchmal die letzte Möglichkeit, eine kaputte Datei zu retten. ls dient zum Erforschen der Directory-Strukturen, mit stats erhält man Einblick in den Superblock. lsdel erzeugt auf Ext2 eine Liste aller gelöschten Dateien. ncheck ermittelt den Dateinamen, der zu einem Inode gehört, berücksichtigt dabei allerdings keine harten Links. icheck zeigt die Inodes an, die auf einen Datenblock verweisen. cat schreibt den Inhalt einer Datei auf den Bildschirm, dump in eine neue Datei. help gibt alle Befehle aus, die debugfs versteht. Man kann das Programm gefahrlos auf gemountete und aktuell genutzte Dateisysteme ansetzen, da es sie standardmäßig nur zum Lesen öffnet. Wer freilich mit [VERBATIM13] schreibend im Dateisystem herumfuhrwerkt, sollte im Hinterkopf behalten, dass man mit diesem Werkzeug die Verwaltungsstrukturen von Ext3 im Handumdrehen so gründlich kaputt kriegt, dass auch e2fsck nichts mehr retten kann.


Fragmentierung messen Um die Fragmentierung einer Datei zu bestimmen, muss man herausfinden, welche Datenblöcke sie belegt, und die Folge der Datenblöcke mit der idealen Blockfolge abgleichen, wie sie das Bild darstellt. Die Datenblöcke einer Datei lassen sich auf drei verschiedene Arten ermitteln. Der direkte Weg besteht darin, die Platte sektorweise einzulesen und die Ext3-Datenstrukturen selbst zu interpretieren. Das läuft letztlich darauf hinaus, große Teile des Ext3-Treibers nachzuprogrammieren. Zum Glück ist das nicht nötig: Die Bibliothek libext2fs stellt Routinen bereit, um direkt auf Funktionen des Dateisystemtreibers zuzugreifen. Die beiden Funktionen ext2fs_get_next_inode() und ext2fs_block_iterate() iterieren über alle benutzten Inodes des Dateisystems und über alle Datenblöcke eines Inodes. ext2fs_get_next_inode() kann man eine eigene Funktion mitgeben, die mit der Blocknummer jedes Datenblocks aufgerufen wird, sodass sich das Layout der Datei auf der Platte mit der idealen Blockfolge vergleichen lässt. Diesen Ansatz wählen e2fsck und unser Tool ext2_frag [8]. ext2_frag muss mit dem Namen des zu testenden Gerätes (Partition oder logisches Volume auf LVM-Systemen) aufgerufen werden: [VERBATIM14] Standardmäßig untersucht das Programm das gesamte Dateisystem und gibt für jede Datei aus, ob und wie sehr sie fragmentiert ist. Mit der Option -f beschränken sich die Ausgaben auf fragmentierte Dateien, was den Output übersichtlicher macht; mit -s werden lediglich zusammenfassende Statistiken für das gesamte Dateisystem ausgegeben. Um also die Fragmentierung des Dateisystems auf /dev/sda1 abzuschätzen, lautet der Aufruf [VERBATIM15] Die Option -i erlaubt es, sich die Fragmentierung einzelner Dateien, identifiziert über ihre Inode-Nummer, im Detail anzusehen; -v und -d liefern dann weitere Details über das Block-Layout der Datei. Alternativ kann man die Blocknummern einer Datei über den Ioctl FIBMAP erfragen, der allerdings nur mit regulären Dateien, nicht mit Verzeichnissen funktioniert. Damit arbeiten die Tools filefrag aus dem E2fsprogs-Paket und fragments [9]. Im Unterschied zu ext2fs_block_iterate() liefert ioctl(FIBMAP) lediglich die Nummern der Datenblöcke zurück, nicht jedoch die der indirekten Blöcke – sie sind lediglich als Lücke in der Folge der Datenblöcke erkennbar. fragments muss mit dem Namen eines Verzeichnisses aufgerufen werden: [VERBATIM16] Mit der Option -r untersucht das Tool rekursiv die Unterverzeichnisse, -d gibt Statistiken für jedes Unterverzeichnis aus. -x sorgt dabei dafür, dass Verzeichnisse, in die andere Dateisysteme gemountet sind, ignoriert werden. Ein typischer Aufruf zur Untersuchung eines ganzen Dateisystems ist [VERBATIM17] Mit -f erhält man Angaben zur Fragmentierung der einzelnen Dateien, mit -b zusätzlich das Blocklayout jeder Datei.


(odi [10])


URL dieses Artikels:
https://www.heise.de/-221480

Links in diesem Artikel:
[1] http://www.heise.de/open/artikel/104859/8
[2] http://e2undel.sf.net
[3] ftp://ftp.heise.de/pub/ct/listings/0805-214.tgz
[4] ftp://ftp.heise.de/pub/ct/listings/0805-214.tgz
[5] ftp://ftp.heise.de/pub/ct/listings/0805-214.tgz
[6] http://www.heise.de/open/artikel/104859/9
[7] mailto:odi@ct.heise.de
[8] ftp://ftp.heise.de/pub/ct/listings/0805-214.tgz
[9] ftp://ftp.heise.de/pub/ct/listings/0805-214.tgz
[10] mailto:odi@ix.de