Organisation des Kernels

Auf einem UNIX-System beschäftigen sich mehrere gleichzeitig laufende Prozesse mit verschiedenen Aufgaben. Jeder Prozeß fordert Systemressourcen an. Das können Rechenzeit, Speicher, Netzwerkverbindungen oder andere Ressourcen sein. Der Kernel ist der große Haufen ausführbaren Codes, der all diese Anforderungen bearbeitet. Obwohl die einzelnen Aufgaben des Kernels nicht klar voneinander getrennt sind, kann die Rolle des Kernels, wie in Abbildung 1-1 gezeigt, in die folgenden Teile aufgeteilt werden:

Abbildung 1-1. Ein Blick auf die Aufgabenblöcke des Kernels

Prozeßverwaltung

Der Kernel ist für das Erzeugen und Zerstören von Prozessen und das Verwalten ihrer Verbindung zur Außenwelt (Input und Output) zuständig. Die Kommunikation zwischen den verschiedenen Prozessen (mit Signalen, Pipes und Primitiven zur Interprozeß-Kommunikation) gehört zur grundlegenden Systemfunktionalität und wird ebenfalls vom Kernel übernommen. Außerdem gehört der Scheduler, der steuert, wie Prozesse sich die CPU teilen, zur Prozeßverwaltung. Allgemein gesprochen, besteht die Prozeßverwaltung des Kernels darin, die Abstraktion mehrerer Prozesse auf einer einzigen CPU (oder auch mehreren) zu implementieren.

Speicherverwaltung

Der Speicher des Computers ist eine wichtige Ressource, und die Verfahren, nach denen sie verteilt wird, sind für die Performance des Systems entscheidend. Der Kernel erzeugt den virtuellen Adreßraum für jeden einzelnen Prozeß aus den vorhandenen beschränkten Ressourcen. Die einzelnen Teile des Kernels interagieren mit dem Subsystem zur Speicherverwaltung über eine Reihe von Funktionsaufrufen, die von einfachen malloc/free-Paaren bis hin zu exotischeren Funktionalitäten reichen.

Dateisysteme

UNIX basiert ganz grundsätzlich auf dem Konzept der Dateisysteme; so ziemlich alles kann unter UNIX als Datei angesehen werden. Der Kernel legt ein strukturiertes Dateisystem über unstrukturierte Hardware, und die daraus resultierende Datei-Abstraktion wird überall im gesamten System verwendet. Linux unterstützt darüber hinaus verschiedene Typen von Dateisystemen, also verschiedene Möglichkeiten, Daten auf dem physikalischen Medium zu organisieren. Beispielsweise können Disketten entweder mit dem Linux-Standarddateisystem ext2 oder mit dem oft verwendeten FAT-Dateisystem formatiert werden.

Gerätesteuerung

So ziemlich jede Systemoperation wird am Ende auf ein physikalisches Gerät zurückgeführt. Mit Ausnahme des Prozessors, des Speichers und einiger weniger anderer Dinge werden sämtliche Gerätesteuerungsoperationen durch Code ausgeführt, der für das jeweilige Gerät spezifisch ist. Dieser Code wird Gerätetreiber genannt. Der Kernel muß einen Gerätetreiber für jedes Peripheriegerät im System enthalten, von der Festplatte über die Tastatur bis hin zum Bandlaufwerk. Um diesen Aspekt der Kernel-Funktionen wird es uns in diesem Buch hauptsächlich gehen.

Netzwerkbetrieb

Der Netzwerkbetrieb muß vom Betriebssystem verwaltet werden, da die meisten Netzwerkoperationen nicht für einen Prozeß spezifisch sind: Eingehende Pakete sind asynchrone Ereignisse. Diese Pakete müssen gesammelt, identifiziert und weitergeleitet werden, bevor sich ein Prozeß darum kümmern kann. Es ist die Aufgabe des Systems, Datenpakete über Programm- und Netzwerkschnittstellen hinweg auszuliefern und die Ausführung von Programmen passend zu ihrer Netzwerkaktivität zu steuern. Außerdem sind alle Belange des Routings und der Adreßauflösung im Kernel angesiedelt.

Ziemlich weit hinten in diesem Buch, in Kapitel 16, finden Sie eine Übersichtskarte über den Linux-Kernel, aber fürs erste sollten diese wenigen Absätze genügen.

Eine der guten Eigenschaften von Linux ist die Fähigkeit, den Kernel-Code dynamisch zur Laufzeit zu erweitern. Das bedeutet, daß Sie Funktionen zum Kernel hinzufügen können, während das System läuft.

Jedes Code-Stück, das zum Kernel hinzugefügt werden kann, wird ein Modul genannt. Der Linux-Kernel unterstützt eine Reihe verschiedener Modul-Typen (auch Klassen genannt), darunter auch Gerätetreiber. Jedes Modul besteht aus Objekt-Code, der nicht zu einem vollständigen ausführbaren Programm gelinkt ist und dynamisch mit dem Programm insmod zum laufenden Kernel hinzugelinkt sowie mit rmmod daraus wieder entfernt werden kann.

In Abbildung 1-1 können Sie die verschiedenen Klassen von Modulen sehen, die jeweils eine bestimmte Aufgabe haben. Die Zugehörigkeit eines Moduls zu einer bestimmten Klasse wird anhand seiner Funktionalität bestimmt. Die Module in Abbildung 1-1 umfassen die wichtigsten Klassen, sind aber lange nicht vollständig, weil immer mehr Funktionalität im Linux-Kernel modularisiert wird.