Das Verzeichnis mm

Das letzte wichtigere Verzeichnis der Kernel-Quelldateien hat mit der Speicherverwaltung zu tun. Die Dateien in diesem Verzeichnis implementieren all die Datenstrukturen, die im gesamten System verwendet werden, um Fragen der Speicherverwaltung zu behandeln. Während die Speicherverwaltung in Registern und anderen Funktionen der jeweiligen CPU stattfindet, haben wir schon in Kapitel 13 gesehen, wie der größte Teil davon plattformunabhängig gemacht worden ist. Interessierte Leser können in asm/arch-arch/mm nachlesen, wie die jeweils niedrigste Ebene für die einzelnen Computer-Plattformen implementiert wird.

Die Allokationsmaschinerie kmalloc/kfree befindet sich in slab.c. Diese Datei ist eine vollständig neue Implementation, die ersetzt, was vorher in kmalloc.c war. Letztere Datei existiert in Versionen nach 2.0 nicht mehr.

Während die meisten Programmierer damit vertraut sind, wie ein Betriebssystem Speicher in Blocks und Seiten verwaltet, greift Linux eine Idee aus Solaris von Sun Microsystems auf und verwendet ein zusätzliches, flexibleres Konzept namens slab. Jeder Slab ist ein Cache, der mehrere Speicherobjekte der gleichen Größe enthält. Manche Slabs sind spezialisiert und enthalten Strukturen eines bestimmten Typs, die von einem bestimmten Teil des Kernels verwendet werden, während andere allgemeiner sind und Speicherbereiche von 32 Bytes, 64 Bytes und so weiter enthalten. Der Vorteil von Slabs besteht darin, daß Strukturen oder andere Speicherbereiche mit nur wenig Verwaltungsaufwand zwischengespeichert und wiederverwendet werden können; die aufwendigere Technik des Allozierens und Freigebens von Seiten wird so seltener benötigt.

Das andere wichtige Allokationswerkzeug, vmalloc, sowie die allem zugrundeliegende Funktion get_free_pages sind in vmalloc.c und page_alloc.c definiert. Beide sind recht einfach strukturiert und interessant zu lesen.

Neben den Allokationsdiensten muß ein Speicherverwaltungssystem auch das Einblenden von Speicher erlauben. Schließlich liegt mmap vielen Aktivitäten des Systems, darunter auch der Ausführung einer Datei, zugrunde. Die Funktion sys_mmap selbst befindet sich aber nicht hier, sondern ist tief im architekturspezifischen Code vergraben, weil Systemaufrufe mit mehr als fünf Argumenten eine Sonderbehandlung in Sachen CPU-Register benötigen. Die Funktion, die mmap für alle Plattformen implementiert, heißt do_mmap_pgoff und findet sich in mmap.c. Diese Datei implementiert auch sys_sendfile und sys_brk. Letztere Funktion mag nicht dazugehörig aussehen, weil brk dazu verwendet wird, die maximale virtuelle Adresse, die ein Prozeß benutzen kann, heraufzusetzen. Linux (und die meisten aktuellen Unix-Systeme) erzeugen neuen virtuellen Adreßraum für einen Prozeß, indem sie Seiten aus /dev/zero einblenden.

Die Mechanismen zum Einblenden einer normalen Datei in den Speicher stehen in filemap.c. Diese Datei arbeitet auf Datenstrukturen ziemlich niedriger Ebene im Speicherverwaltungssystem. mprotect und remap werden in zwei Dateien gleichen Namens implementiert; das Sperren von Speicher taucht in mlock.c auf.

Wenn ein Prozeß mehrere aktive Speichertabellen hat, braucht man eine effiziente Möglichkeit, im Adreßraum nach freien Adressen zu suchen. Dazu sind alle Speichertabellen eines Prozesses als Adelson-Velski-Landis-Baum (AVL-Baum) aufgebaut. Diese Software-Struktur wird in mmap_avl.c implementiert.

Die Initialisierung und Entfernung von Auslagerungsdateien (also die Systemaufrufe swapon und swapoff) sind in swapfile.c zu finden. swap_state.c kümmert sich um den Auslagerungs-Cache und swap.c um das Altern von Seiten. Das Auslagern selbst ist hier nicht definiert, sondern ist ein Bestandteil der Verwaltung von Speicherseiten, was durch den Thread kswapd implementiert wird.

Die niedrigste Ebene der Seitentabellenverwaltung wird in der Datei memory.c implementiert, die immer noch die ursprünglichen Anmerkungen von Linus enthält, als er im Dezember 1991 die erste echte Speicherverwaltung implementierte. Alles, was sich auf noch niedrigeren Ebenen abspielt, befindet sich in Architektur-spezifischem Code (oft als Makros in den Header-Dateien versteckt).

Der Code zur Verwaltung von hohem Speicher befindet sich in highmem.c, wie Sie sicherlich unschwer erraten können.(Unter “hohem Speicher” versteht man Speicher jenseits der Grenze, die der Kernel direkt adressieren kann. Diese Speicheraufteilung wird insbesondere in der x86-Welt verwendet, um mehr als 4 GB RAM unterstützen zu können, ohne die 32-Bit Architektur aufzugeben).

vmscan.c implementiert den kswapd-Kernel-Thread. Dies ist die Prozedur, die nach unbenutzten oder alten Seiten sucht, um diese freizugeben oder in den Auslagerungsbereich zu stecken. Die Quelldatei ist wohlkommentiert, weil das Fine-Tuning dieser Algorithmen ein Schlüsselfaktor für die gesamte System-Performance ist. Hier ist keine Entwurfsentscheidung trivial oder unkritisch und alles will gut durchdacht sein, weswegen hier so viele Kommentare stehen.

Der Rest der Quelldateien im mm-Verzeichnis kümmert sich um kleinere, aber manchmal wichtige Details wie oom_killer, eine Prozedur, die bestimmt, welcher Prozeß zwangsweise beendet wird, wenn das System keinen Speicher mehr zur Verfügung hat.

Interessanterweise führt die uClinux-Portierung des Linux-Kernels auf MMU-lose Prozessoren ein weiteres Verzeichnis namens mmnommu ein. Dieses wiederholt den offiziellen mm-Code recht genau, läßt aber allen MMU-bezogenen Code weg. Die Entwickler haben sich für diese Lösung entschieden, um einen Wust bedingter Kompilierungsanweisungen im mm-Baum zu vermeiden. Weil uClinux (noch) nicht in den Mainstream-Kernel integriert ist, müssen Sie einen uClinux-CVS-Baum oder ein tar-Paket herunterladen, wenn Sie die beiden Verzeichnisse vergleichen wollen (beide sind im uClinux-Baum enthalten).