zurück zum Artikel

Linux 5.16 beschleunigt Spiele und steigert die Systemleistung

Oliver Müller

(Bild: J0hnTV/Shutterstock.com)

Linux 5.16 steht im Zeichen von Leistungssteigerung, Optimierung und Modernisierung. Neu hinzukommen sind unter anderem futex2 und DAMON Operation Schemes.

Linux 5.16 ließ sich eine Woche länger Zeit als ursprünglich erwartet. Linus Torvalds entschied sich, dem Kernel etwas mehr Zeit zum Reifen zu geben. Auslöser waren nicht etwa Probleme oder alarmierende Testergebnisse, sondern schlicht die Sorge, das Testen könnte infolge der Feiertage und der Woche "zwischen den Jahren" zu knapp kommen.

Tatsächlich kamen in der zusätzlichen Woche mit rc8 keine großen Änderungen mehr auf den Tisch. Der neue Kernel erblickte in der Nacht zum Montag das Licht der Welt und war eher als Wartungsrelease erwartet worden. Dennoch bietet der 5.16 einige interessante Neuerungen wie den Einstieg in futex2, aktives Alarmieren von Dateisystemfehlern auf Kernel-Ebene, einen "cluster-bewussten" CPU-Scheduler und erhöhte Sicherheit. Auch eine Mammutaufgabe durch Folio-Pages für eine effiziente und leistungssteigernde Speicherverwaltung geht der neue Kernel an.

Linux 5.16 gibt den Startschuss für futex2 und verspricht insbesondere bei der Emulation von Windows-Spielen bessere Performance. futex2 soll auf lange Sicht den in die Jahre gekommenen und historisch gewachsenen System-Call futex() ablösen. Futex steht für "Fast User Mutex". Es erlaubt Mutexes, also Sperren für den konkurrierenden Zugriff, nicht nur im Kernel-Space auch im Userspace zu verwenden.

Das Konzept wurde bereits 2003 im Kernel 2.6.0 in Form des System-Calls futex() eingeführt. Erste zentrale Anwendung fand der System-Call fürs Steuern der Nebenläufigkeit von POSIX-Threads. Zuvor konnten Userspace-Prozesse lediglich auf Semaphoren beispielsweise über eventfd() beim Zugriff auf gemeinsam genutzte Ressourcen bauen.

Semaphoren bieten einen Mechanismus, um frei gewordene Ressourcen zu "signalisieren". Ein auf eine frei werdende Ressource wartender Prozess oder Thread prüft regelmäßig, ob der Zugriff nun möglich ist. Mutexes hingegen sperren Ressourcen, bis diese wieder frei werden. Ein Aufruf von futex() blockiert den Prozess beziehungsweise Thread solange, bis die betreffende Ressource frei wird. Mutexes sind daher grundsätzlich in der Anwendung schlanker und im Kernel CPU-defensiver als Semaphoren.

Durch die Beliebtheit von futex() kommt der System-Call in vielen Anwendungsszenarien zum Einsatz. Daher wurden Rufe nach einem Reengineering des Calls lauter, um anwendungsfallbezogene einzelne Funktionen oder zumindest Wrapper in der libc zu erhalten. Als Reaktion folgte vor rund zwei Jahren der futex2-Ansatz von André Almeida mit dem Ziel einen flexibles Futex-Subsystem zu schaffen.

Von einem Ersatz für das alte futex() ist futex2 auch in Linux 5.16 noch weit entfernt. Der eingeflossene Ansatz ist eine Light-Variante, der ein spezifisches Problem adressiert. Das bisherige futex() ist in seiner Funktion beschränkt. Es kann lediglich einen Futex abfragen. Sind mehrere zu berücksichtigen, geht das nur hintereinander. Mit dem Manko in mehrere blockierende Aufrufe von futex() in Serie zu laufen. Das hat gerade bei portierten Windows-Spielen Nachteile, die Emulationsschichten wie WINE oder Protons Steam Play nutzen.

Die Windows-API bietet die Funktion WaitForMultipleObjects() [1], die das Abfragen mehrerer Objekte erlaubt. Diese Objekte lassen sich analog zu den Ressourcen und Futexes in Linux verwenden. Windows kann die Abfrage aber in einem Aufruf parallel durchführen. Linux bislang nicht. An dieser Stelle setzt Linux 5.16 mit dem ersten futex2-Patch [2] an. Der Kernel führt den System-Call futex_waitv() ein, der – anders als futex() – auf mehrere Mutex beziehungsweise deren Freigabe warten kann. Damit kann Linux 5.16 in Emulationsschichten WaitForMultipleObjects() eleganter und effizienter nachbilden. Portierte Spiele profitieren von einem Geschwindigkeitsgewinn.

André Almeida sieht womöglich das Potential [3], auch native Linux-Spiele mithilfe des neuen System-Calls zu beschleunigen: "Native Game-Engines werden ebenfalls davon profitieren, vorausgesetzt dieses Wait-Muster ist üblich für Spiele." Eine Dokumentation von futex2 und futex_waitv() findet sich in den entpackten Kernel-Sourcen unter Documentation/userspace-api/futex2.rst, alternativ auch im betreffenden Patch [4].

Zum Überwachen, Benachrichtigen sowie Unterbinden von Zugriffen auf Dateien und Verzeichnisse existiert das Kernel-Modul fanotify (file access notify). Ursprünglich als Schnittstelle für Echtzeitvirenscanner eingeführt, mausert sich das Modul schon länger zum allgemeinen Überwachen von Dateizugriffen. So ist es neben dem Virenscan, beispielsweise über ClamAV, auch beim readahead von systemd im Einsatz, bei dem Inhalte von Dateien auf "Vorrat" ins RAM eingelesen werden, um den Zugriff auf die Daten zu beschleunigen.

In Linux 5.16 kann fanotify nicht nur den Zugriff auf Dateien und Verzeichnisse überwachen. Es wächst endgültig über seinen ursprünglichen Einsatzzweck hinaus und kann nun auch Dateisystemfehler erkennen und alarmieren. Monitoring-Systeme können sich so aktiv benachrichtigen lassen, um im Anschluss den Systemadministrator zu informieren oder auch selbsttätig Reparaturoperationen einzuleiten.

Die Dokumentation zum Dateisystem-Monitoring mittels fanotify findet sich in den entpackten Kernel-Sources unter Documentation/admin-guide/filesystem-monitoring.rst. Zudem findet sich im Verzeichnis samples/fanotify ein Beispielprogramm in C, wie sich das neue Feature in eigenen Programmen nutzen lässt.

Das Verwalten des virtuellen Arbeitsspeichers erfolgt in Linux in Speicherseiten (Pages). Historisch waren diese beim ersten Linux-Kernel auf feste 4.096 Bytes gesetzt, dem damals seitens des i386 möglichen und optimalen Werts. Moderne Prozessorarchitekturen erlauben heute wesentlich größere Pages. Die x86-Architektur kann heute mit Pages von 2 Megabytes umgehen. Andere CPU-Architekturen wiederum nur mit Kleineren oder aber auch mit wesentlich Größeren.

Die Größe der Pages spielt für die Leistung des Systems eine Rolle, da sich mit dem optimalen Wert die Hardware bestmöglich bedienen lässt. Der Linux-Kernel läuft auf einer Vielzahl unterschiedlicher Architekturen. Da jede Architektur eigene, teils sehr unterschiedliche Einstellungen für die Page-Größe erlaubt, nutzt Linux intern zu Verwaltungszwecken eine (logische) Basisgröße für Pages – die alten 4.096 Bytes. Die Größe unterstützen alle Prozessorarchitekturen direkt oder zumindest als Vielfaches davon.

Um die hardware-implizierte Page intern abzubilden, nutzt der Linux Kernel Verbünde von Pages (Compound-Pages). In diesen "Verbünden" hängt er logische Basis-Pages aneinander, um auf die Größe der physischen Seiten zu kommen. Dabei gibt es eine Head-Page, die den Anfang des Speicherbereichs repräsentiert. Alle folgenden Pages im Verbund sind die Tail-Pages.

Linux verwaltet alle Pages durch eine C-Struktur struct page. Syntaktisch oder per Typprüfung lässt sich nicht unterscheiden, ob eine Variable vom Typ page eine Head- oder eine Tail-Page repräsentiert. In Kernel-Funktionen stellt das ein Problem dar. Wird eine Head-Page als Parameter übergeben, ist der Fall für die Kernel-Funktion klar. Sie kann direkt mit der Compound-Page arbeiten. Kommt jedoch eine Tail-Page in der Kernel-Funktion an, muss zunächst die Head-Page ermittelt werden. Das geht zwar einfach über die Funktion compound_head(), bleibt aber nicht folgenlos.

In unzähligen Kernel-Funktionen findet dieser Aufruf statt. Um wenigstens ein paar Taktzyklen zu sparen, ist compound_head() als Inline-Funktion umgesetzt. Es findet im ausführbaren Kernel kein echter Funktionsaufruf statt, sondern der Code ist in jede nutzende Kernel-Funktion hineinkopiert. Letzteres bläht den Kernel schließlich in Summe auf.

Um diese Ineffizienz zu beseitigen, entwickelte Matthew Wilcox das Folio-Konzept. Eine Folio-Page ist nicht anderes als eine Page, die garantiert eine Head-Page ist. Prüfungen und Konvertierungen mittels compound_head() fallen weg. Gerade bei vielen Funktionen des Kerns der Speicherverwaltung, aber auch bei Dateisystemen, kann das den Code verschlanken und die Leistung steigern helfen.

Der Ansatz wurde lange kontrovers diskutiert, denn er zieht einen Gewaltakt nach sich. Einerseits wird eine Parallelwelt für die Page-Verwaltung geschaffen. Neben "normalen" bestehenden Page-Funktionen, wie beispielsweise put_page() und get_page(), sind Folio-Funktionen, wie put_folio() und get_folio(), notwendig. Anschließend sind Unmengen von Kernel-Funktionen mit Auswirkungen auf die API anzupassen.

Für Linux 5.15 waren noch nicht alle Bedenken ausgeräumt und somit Folio nicht mit von der Partie. Die erzielbare Leistungssteigerung überzeugte schließlich für Linux 5.16 und ließ das Kernel-Team den Gewaltakt wagen. Linux 5.16 verfügt über Folio-Pages, die sich nicht nur in Spezialfällen, sondern im normalen Betrieb positiv auswirken.

Der vorhergehende Linux-Kernel 5.15 schaffte mit dem "Data Access MONitor" (DAMON) einen wichtigen Bestandteil des "Data Access-aware Operating System" [5] (DAOS). Das Ziel von DAOS ist es, über aus dem laufenden System ermittelte Zugriffsmuster die Performance und Effizienz des Datenzugriffs durch das Betriebssystem zu optimieren. DAMON kommt hierbei die Aufgabe zu, die Arbeitsspeicherzugriffe und -nutzung von Userspace-Prozesse zu überwachen und über die ermittelten Zugriffsmuster die Grundlage für die Optimierung zu schaffen.

DAMON war bereits in Linux 5.15 in der Lage Profile von Userspace-Prozessen anzulegen. Mit den "DAMON Operating Schemes" (DAMOS) folgen nun auch Taten. Über Zugriffsmuster lassen sich nun Aktionen in Form von madvice()-Aufrufen auslösen [6]. Der Kernel kann anschließend adäquate Strategien wie Caching, Paging oder Read-Ahead für die jeweiligen Speicherbereiche ergreifen.

Neu ist der DAMON_RECLAIM, der länger ungenutzte Speicherbereiche (cold memory regions) identifiziert, umgehend auslagert (page out) und so wieder schneller für andere Tasks bereitstellt. Der Mechanismus ist für Situationen mit leichterem Engpass bei physischem Speicher gedacht. Das dahinterstehende Konzept belastet die CPU dabei weniger als das übliche Identifikations- und Auslagerungsverfahren. Eine ausführliche Dokumentation findet sich in den entpackten Kernel-Quellen unter Documentation/admin-guide/mm/damon/reclaim.rst. Zusätzlich kann das Monitoring nun mit physischen Speicheradressen umgehen. Zuvor konnten nur virtuelle Speicheradressen überwacht werden.

Einige Systeme verwenden spezielle Hardware-Topologien, bei denen sich mehrere CPU-Kerne L3-Tags oder L2-Caches teilen (CPU-Cluster). Bislang berücksichtigte der CPU-Scheduler von Linux beim Verteilen von Tasks derartige Cluster nicht, was zu ungleich ausgelasteten Caches führen kann. In Linux 5.16 ist sich der Scheduler der über Caches zusammenhängenden CPU-Kerne bewusst und verteilt die Tasks so, dass auch systemweit die Caches ausgeglichen belastet sind. Die Leistung auf den betreffenden Systemen lässt sich damit wesentlich steigern.

Auf PowerPC-Systemen ist die Option STRICT_KERNEL_RWX in der Kernel-Konfiguration per Standard aktiv. Somit sind alle Kernel-Speichersegmente voreingestellt entweder ausführbar oder schreibar. Das verhindert, dass im Falle von Buffer-Overflows dem Kernel Code untergeschoben werden kann.

Mit dem neuen Kernel ist Memory-Hotplug auf 32-Bit-x86-Systemen nicht mehr möglich. Das Feature erlaubt, die Größe des verfügbaren physischen Arbeitsspeichers eines Systems zur Laufzeit zu erhöhen oder zu verringern. Wichtig ist das in der Virtualisierung, um für virtuelle Maschinen dynamisch den Speicher anpassen zu können.

Das Memory-Hotplug auf 32-Bit-x86 war bereits seit über einem Jahr nicht mehr funktionsfähig und daher als defekt markiert. Nachdem das keiner beklagte, wurde das Feature nun gestrichen. Auf anderen CPU-Architekturen ist Memory-Hotplug weiterhin verfügbar.

Für die kommenden Systemgenerationen ist Linux mit den neuen Release für Intels "Advanced Matrix Extensions" (AMX) vorbereitet. Die Erweiterung bringt neue Register und Befehle für Matrizen-Operationen. Im ARM-Bereich hält die "Arm 8.6 timer extension" [7] Einzug. MIPS erhält einen neuen Just-in-Time-Compiler für BPF. Zu guter Letzt beherrscht nun auch RISC-V KVM (Kernel-based Virtual Machine).

Schließlich härtet sich Linux gegen klassische Angriffe. Buffer-Overflows nimmt die zusätzliche Bereichsprüfung in strict_memcpy() weitere Angriffspunkte. Zudem führt das neue Kernel-Release die neue Funktion cc_platform_has() [8] ein. Diese generische Schnittstelle gibt im Kernel-Code fortan Auskunft darüber, welche Merkmale im Bereich "Confidential Computing" verfügbar und aktiv sind.

Das Confidential Computing gestattet es, über Hardware abgesicherte und kryptographisch abgeschottete Enklaven im System zu schaffen. Als erster Anwendungsfall löst cc_platform_has() bereits jetzt mem_encrypt_active() ab, das bislang anzeigte, ob der Arbeitsspeicher verschlüsselt ist.

Linux 5.16 bringt keine große Fülle an neuen Funktionen, Features und Technologien für Endbenutzer. Auch neue Anwendungen, Integration neuer technischer Bereiche oder gar Killer-Apps sollte man nicht erwarten. Ein reines Wartungs-Update, das lediglich Bugfixes und neue oder verbesserte Treiber liefert, bleibt es dennoch nicht.

Der neue Kernel glänzt deutlich mit neuen Ansätzen zur Performance-Steigerung. futex2 beschleunigt zwar "nur" den Spezialbereich der portierten Windows-Games. Folio-Pages greifen hingegen tief in den Kernel ein und stellt damit Weichen für ein schlankeres und schnelleres System. Ebenso schlägt der Scheduler mit dem Berücksichtigen der CPU-Cluster in die Performance-Kerbe.

Linux 5.16 steht damit im Zeichen der Optimierung und Leistungssteigerung. Aber auch in puncto Modernisierung fasst sich das Entwicklerteam ein Herz. Historisch gewachsene Konzepte wie das überfrachtete futex() werden entstrubbelt. Um zu modernisieren und zu entschlacken, geht das Entwicklerteam auch aufwendige Aufgaben wie die Folio-Pages an. Die jetzt angestoßenen Umbauten werden auch die kommenden Linux-Versionen noch beschäftigen, wenn nicht gar das eine oder andere Release prägen. Eine Übersicht der hier behandelten - und weiterer Änderungen findet sich im ChangeLog des Kernels [9]. (axk [10])


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

Links in diesem Artikel:
[1] https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitformultipleobjects
[2] https://lore.kernel.org/all/20210805190405.59110-1-andrealmeid@collabora.com/
[3] https://lore.kernel.org/all/20210805190405.59110-1-andrealmeid@collabora.com/
[4] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/diff/Documentation/userspace-api/futex2.rst?id=v5.16&id2=v5.15
[5] https://www.heise.de/hintergrund/Linux-5-15-Kernel-Features-kennenlernen-und-ausprobieren-6271701.html
[6] https://man7.org/linux/man-pages/man2/madvise.2.html
[7] https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/arm-architecture-developments-armv8-6-a
[8] https://git.kernel.org/linus/46b49b12f3fc
[9] https://cdn.kernel.org/pub/linux/kernel/v5.x/ChangeLog-5.16
[10] mailto:axk@heise.de