Linux 6.1 als nächster Langzeit-Kernel erschienen

Der neue Kernel 6.1 bereitet den Weg für in Rust programmierte Module. Er bietet ein besseres Speicher-/Swap-Management und legt bei eBPF und Fehlersuche nach.

In Pocket speichern vorlesen Druckansicht 67 Kommentare lesen

(Bild: heise online)

Lesezeit: 9 Min.
Von
  • Oliver Müller
Inhaltsverzeichnis

Nachdem der Vorgänger Linux-Kernel 6.0 ein Wartungs-Release war, stellt der neue Kernel 6.1 die Weichen für die Zukunft. Mittel- bis langfristig wird Kernel-Funktionalität nicht mehr nur in C, sondern auch in Rust programmiert werden können. Neue Möglichkeiten beim Aufspüren von Fehlern im Kernelspace und ein neues Swap-Management setzen neue Akzente.

Die wohl größte Neuerung in Linux 6.1 ist der Einzug von Rust. Schon einige Jahre wurde hinter den Kulissen gefeilt und gehämmert, um den Kernel für die aufstrebende Programmiersprache fit zu machen. So soll es zukünftig möglich sein, nicht nur Module in C zu programmieren, sondern auch in Rust.

Der aktuelle Stand erlaubt es bislang lediglich, ein sehr einfaches Beispielmodul zu kompilieren, laden und entladen. Linus Torvalds wollte im ersten Schritt lediglich ein einfaches "Hello, world!". Andere und vor allem sinnvolle Module sind damit in Rust im aktuellen Kernel nicht möglich. Dennoch gibt der neue Kernel einen ersten Einblick, wie die Integration von Rust erfolgt und welche Herausforderungen gemeistert werden wollen.

Typisch für den Linux-Kernel bedingt die Rust-Integration bestimmte Versionen: Ein erfolgreicher Einbau von Rust in den Kernel garantieren nur das (ältere) Rust 1.62.0 und bindgen 0.56.0. Wer das Wagnis mit neueren Versionen eingehen möchte, dem quittiert der Kernel-Konfigurationsprozess dieses mit einer Warnmeldung. Ein Abbruch erfolgt jedoch nicht.

Neben dem im Pfad verfügbaren Rust und bindgen ist der Quellcode der Rust-Standard-Library nötig, da im Zuge der Kernel-Build-Prozesses eigene Versionen der Crates (Rust-Modul) core und alloc erstellt werden. Liegen die Werkzeuge und der Quelltext nicht vor, deaktiviert der Kernel-Konfigurationsprozess stillschweigend die Rust-Unterstützung. Die Rust-Option in "make menuconfig" verschwindet kurzerhand.

Wer Rust zu den vorgegebenen Versionen konform einbinden möchte, kann selten auf die Pakete seiner Linux-Distribution zurückgreifen. Es empfiehlt sich aktuell, Rust in der passenden Version über rust-lang.org zu installieren. Voraussichtlich werden die Linux-Distributoren später Softwarepakete für die Rust-Integration bereitstellen.

Die Rust-Integration ist bei Weitem nicht vollständig. Im Wesentlichen ist nur verfügbar, was als minimale Basis für das Beispielmodul nötig ist. Das Zusammenführen von C und Rust im Kernel stellt noch immer eine Herausforderung dar. Die im Kernel gewachsenen Strukturen und Mechanismen sind auf die Möglichkeiten und Eigenheiten von C ausgelegt. Zwar ist das Zusammenspiel von Rust und C mithilfe von bindgen im Userspace schon seit den Kindertagen von Rust möglich. Die Kernel-API stellt Rust jedoch vor ganz neue Herausforderungen.

Besonders deutlich zeigen das Strukturen in der Kernel-API, die Zeiger auf Funktionen enthalten. Ein Klassiker ist die C-struct file_operations, die auf Funktionen eines Treibers verweist. Grundsätzlich kann diese auch Rust mit "traits" darstellen. Allerdings verwendet der Kernel hier gerne Nullzeiger, wenn die betreffende Operation vom Treiber nicht implementiert ist. Der C-Code des Kernels erkennt diese Nullzeiger und führt an den betreffenden Stellen stattdessen Default-Operationen aus. Rust versucht derartige Nullzeiger-Konstruktionen zu vermeiden, was zusätzlichen Aufwand beim Einbinden von Rust in den Kernel erzeugt. Der Linux-Kernel sieht hierfür ein komplexes Makro #[vtable] vor, um Rust in diesem Punkt möglichst geschmeidig Kernel-kompatibel zu machen.

Da Torvalds in Linux 6.1 nur ein "Hello, world!"-Demo wollte, sind diese nutzbringenden Konstrukte erst für 6.2 vorgesehen. Die Lücke zwischen Demo und nutzbarer Rust-Umgebung wird ein Patch-Set des Entwicklers Miguel Ojeda in 6.2 grundsätzlich schließen. Bis die ersten Treiber als Rust-Code in den Kernel einfließen, werden jedoch noch einige Releases kommen und gehen.

Programme können auf modernen Systemen mit Linux mehr Arbeitsspeicher verwenden als physisch vorhanden ist. Wird der "echte" Speicher knapp, schiebt Linux Daten aus dem Arbeitsspeicher in den Swap, also auf die Festplatte. Bei der Auswahl, was in den Swap umziehen soll, nutzt die Speicherverwaltung von Linux den LRU-Ansatz (Least Recent Used). Es nutzt zwei Warteschlangen von Speicherseiten (Pages). Eine mit den aktiven Pages, also jene, die zuletzt verwendet wurden. Und eine mit den inaktiven Speicherseiten, von denen das System "glaubt", sie seien nicht in Gebrauch. Wird auf eine inaktive Page zugegriffen, wandert sie zu den aktiven Pages. Braucht das System Speicher, werden die inaktiven Pages ihrer Reihenfolge nach zu Swap-Kandidaten.

Dieser Ansatz erzielt in der Praxis nicht immer die besten Ergebnisse. Beispielsweise können so einige Pages in einem ineffizienten Reigen zwischen inaktiven und aktiven Pages durchrotieren. Linux 6.1 verspricht mit dem Multi-Generational LRU (MGLRU) eine verbesserte Strategie. Statt zwei Listen bündelt das System Pages in mehrere Schichten von Listen, Generationen genannt. Darin hält das System fest, wann eine Page zuletzt verwendet wurde. Hierzu scannt das System die Pages regelmäßig. Was bei wiederholten Scans bereits "aktiv" war, wandert eine Generation höher. Was nicht im Zugriff war, geht Richtung älteste Generation. Braucht das System Speicher, findet es Swap-Kandidaten in der ältesten Generation.

Zudem arbeitet MGLRU beim Scan über die Prozess-Page-Tabellen, nicht direkt mit dem physischen Speicher. Das erhöht laut Yu Zhao, dem Entwickler von MGLRU, die Effizienz. MGLRU ist vorsichtshalber mit einem "Kill-Switch" versehen. Über /sys/kernel/mm/lru_gen/enabled lässt sich das neue Memory-Management deaktivieren, sollte es Probleme im System bereiten.