3 Punkte von GN⁺ 2025-11-05 | Noch keine Kommentare. | Auf WhatsApp teilen
  • Erklärt die Struktur des Prozessspeichers unter Linux auf Ebene des tatsächlichen Verhaltens und erläutert Schritt für Schritt die Beziehung zwischen virtuellem Adressraum und physischem Speicher
  • Beschreibt anhand zentraler Mechanismen wie Seitentabellen, VMA, mmap, Page Fault, CoW, wie Prozesse Speicher konkret besitzen und darauf zugreifen
  • Zeigt, wie sich über das /proc-Dateisystem der Speicherzustand pro Prozess beobachten lässt, und stellt die Rolle fortgeschrittener Diagnosewerkzeuge wie pagemap und kpageflags vor
  • Behandelt Performance-Optimierung und Dirty-Tracking im User Space mit aktuellen Kernel-Funktionen wie Transparent Huge Pages (THP), userfaultfd und PAGEMAP_SCAN
  • Erklärt außerdem sicherheits- und performancebezogene Kernel-Designprinzipien wie PTI als Gegenmaßnahme gegen Meltdown, TLB-Flushes und die W^X-Richtlinie und vermittelt so ein Gesamtverständnis des Linux-Speichermanagements

Grundstruktur des Prozessspeichers

  • Wenn ein Programm ausgeführt wird, wirkt es so, als gäbe es einen riesigen zusammenhängenden Speicherbereich, tatsächlich wird dieser aber vom Linux-Kernel dynamisch in Seiteneinheiten aufgebaut
    • Die CPU schlägt in der Seitentabelle nach und wandelt virtuelle Adressen in physische Frames um
    • Gibt es keine Zuordnung, tritt ein Page Fault auf, und der Kernel weist eine neue Seite zu oder gibt einen Fehler zurück
  • Wenn der physische RAM knapp wird, verschiebt der Kernel ungenutzte Seiten auf den Datenträger oder entfernt Dateiseiten, um Platz zu schaffen
  • /proc ist ein virtuelles Dateisystem, das der Kernel im Speicher aufbaut und das Prozess- und Kernelzustände in Dateiform offenlegt

Adressraum und VMA

  • Jeder Prozess besitzt ein Adressraumobjekt, das intern aus mehreren VMA (Virtual Memory Areas) besteht
    • Eine VMA ist ein zusammenhängender Adressbereich mit denselben Rechten (R/W/X) und demselben Backend (anonymer Speicher oder Datei)
  • Seitentabellen sind die von der Hardware referenzierten Strukturen und speichern die Zuordnungsinformationen (PTE) zwischen virtuellen und physischen Seiten
  • Änderungen am Adressraum erfolgen über drei Systemaufrufe
    • mmap: neuen Bereich anlegen
    • mprotect: Rechte ändern
    • munmap: Mapping entfernen
  • Seiten haben 4 KiB als Basiseinheit, einige Systeme unterstützen auch große Seiten mit 2 MiB oder 1 GiB

Speicheraufbau mit /proc/self/maps ansehen

  • Mit dem Befehl cat /proc/self/maps lässt sich die Speicherbelegung eines Prozesses prüfen
    • Sichtbar sind Code, Daten und bss der ausführbaren Datei, Heap, anonyme Mappings, Shared Libraries, Stack usw.
  • Die Bereiche [vdso] und [vvar] sind vom Kernel gemappter Code und Daten für schnelle Systemaufrufe

Funktionsweise von mmap

  • mmap ist keine unmittelbare Speicherallokation, sondern eine vermerkte Zusage im Adressraum
    • Seiten werden erst beim ersten Zugriff zugewiesen
  • Bei Dateimappings muss offset seitenaligned sein, und ein Zugriff über das Dateiende hinaus löst SIGBUS aus
  • MAP_SHARED wird direkt in die Datei zurückgeschrieben, MAP_PRIVATE erzeugt über Copy-on-Write (CoW) unabhängige Seiten
  • MAP_FIXED_NOREPLACE sorgt für mehr Sicherheit, indem es fehlschlägt, wenn an der angegebenen Adresse bereits ein Mapping existiert

Erster Zugriff und Page Fault

  • Beim ersten Zugriff auf ein neues Mapping findet die CPU keinen Eintrag in der Seitentabelle, wodurch ein Page Fault entsteht
    • Der Kernel prüft die Gültigkeit der Adresse, Zugriffsrechte und das Vorhandensein des Bereichs
    • Bei anonymem Mapping wird eine neue, mit Nullen gefüllte Seite zugewiesen, bei Dateimapping wird aus dem Page Cache gelesen
  • Ein minor fault liegt vor, wenn die Daten bereits im RAM sind, ein major fault, wenn Platten-I/O nötig ist
  • Der Stack ist durch eine Guard Page geschützt; ein zu weit nach unten gehender Zugriff führt zu SIGSEGV

fork() und Copy-on-Write bei MAP_PRIVATE

  • Beim fork teilen sich Eltern- und Kindprozess dieselben physischen Seiten, die beide als read-only markiert werden
    • Erst beim Schreiben wird eine neue Seite kopiert, damit beide unabhängig bleiben
  • Dateimappings mit MAP_PRIVATE arbeiten nach demselben Prinzip
  • Relevante Optionen
    • vfork: gemeinsamer Adressraum mit dem Elternprozess
    • clone(CLONE_VM): erzeugt Threads
    • MADV_DONTFORK, MADV_WIPEONFORK: Mapping im Kindprozess ausschließen oder mit Nullen initialisieren

Rechte ändern und TLB-Invalidierung

  • Wenn mprotect Seitenrechte ändert, führt der Kernel eine Aufteilung der VMA und Änderungen an den Seitentabellen durch und invalidiert anschließend den TLB
  • Gemäß der W^X-Richtlinie darf eine Seite nicht gleichzeitig beschreibbar und ausführbar sein
  • Der TLB (Translation Lookaside Buffer) ist ein Cache für aktuelle Adressübersetzungen; seine Invalidierung verursacht kurzzeitig Verzögerungen

Detaillierte Beobachtung über /proc

  • Mit /proc/<pid>/maps, smaps und smaps_rollup lassen sich Rechte, RSS und HugePage-Nutzung pro Bereich prüfen
  • /proc/<pid>/pagemap liefert Zustände auf Seitenebene (vorhanden, geswappt, PFN usw.), PFNs sind für normale Benutzer jedoch nicht sichtbar
  • /proc/kpagecount und /proc/kpageflags zeigen pro PFN die Anzahl der Mappings und Seiteneigenschaften wie anonym, Datei, dirty usw.
  • Mit mincore und SEEK_DATA/SEEK_HOLE lassen sich Daten- und Lochbereiche in Sparse Files identifizieren
  • Durch die Kombination von PAGEMAP_SCAN und userfaultfd lässt sich Dirty-Tracking im User Space implementieren

Transparent Huge Pages (THP) und mTHP

  • THP bündelt häufig genutzten Speicher automatisch zu großen Seiten (z. B. 2 MiB), um die TLB-Effizienz zu verbessern
    • Der Thread khugepaged fasst benachbarte Seiten zusammen
  • mTHP unterstützt variable große Seiten (Folio-Größen) wie 16 KiB oder 64 KiB
  • Ob dies genutzt wird, lässt sich in /proc/self/smaps über AnonHugePages und FilePmdMapped prüfen
  • Systemweite Einstellungen werden unter /sys/kernel/mm/transparent_hugepage/ verwaltet
  • Mit MADV_HUGEPAGE und MADV_NOHUGEPAGE ist eine Steuerung pro Bereich möglich

Dirty-Tracking im User Space

  • Mit userfaultfd und PAGEMAP_SCAN lassen sich nur geänderte Seiten kopieren
    • Der Kernel führt Scannen und Schreibschutz in einer einzigen atomaren Operation aus
    • Das ist effizient für Snapshots, Live-Migration und ähnliche Szenarien

Mechanismus von TLB-Flushes

  • Auf x86 erfolgt die TLB-Invalidierung auf zwei Arten
    • INVLPG: invalidiert eine einzelne Seite
    • vollständiger Flush durch erneutes Laden der Wurzel der Seitentabelle
  • PCID und INVPCID ermöglichen die Verwaltung prozessbezogener TLB-Tags und reduzieren unnötige Flushes
  • tlb_single_page_flush_ceiling ist der Schwellenwert, anhand dessen der Kernel zwischen seitenweisem und vollständigem Flush wählt

Gegenmaßnahme gegen Meltdown: Page Table Isolation (PTI)

  • Meltdown ist eine Schwachstelle, bei der Kernel-Daten während spekulativer Ausführung über den Cache offengelegt werden können
  • Linux trennt mit PTI (Page Table Isolation) den Benutzer- und Kernel-Adressraum
    • Beim Eintritt wird über einen CR3-Wechsel eine kernel-exklusive Seitentabelle verwendet
    • PCID hilft dabei, TLB-Flushes zu minimieren
  • PTI ist standardmäßig aktiviert und kann mit nopti deaktiviert werden

Sicheres Vorgehen des Kernels bei Mapping-Änderungen

  • Bei Änderungen an Mappings ist die Reihenfolge
    1. Cache-Regeln behandeln
    2. Seitentabellen ändern
    3. TLB invalidieren
  • Auch bei kernelinternen Mappings (vmap, vmalloc) werden vor und nach I/O Cache und TLB synchronisiert
  • Auf einigen Architekturen ist nach dem Kopieren von Code ein Instruction-Cache-Flush nötig

Stack- und Aufrufstruktur auf x86

  • Im 64-Bit-Modus werden die Register RIP, RSP und RBP verwendet, der Stack wächst nach unten
  • Nach der System V AMD64 ABI werden Argumente über RDI, RSI, RDX, RCX, R8, R9 übergeben, Rückgabewerte über RAX
  • Der User Mode läuft in Ring 3, der Kernel in Ring 0; Systemaufrufe und Interrupts wechseln über Gates dazwischen

Fehlersituationen und Diagnose

  • mmapEINVAL: Fehler bei der Ausrichtung des Datei-Offsets
  • mmapENOMEM: zu wenig virtueller Raum oder Overcommit-Beschränkung
  • Zugriff auf Dateimapping → SIGBUS: Zugriff hinter EOF
  • mprotect(PROT_EXEC)EACCES: noexec-Mount oder W^X-Richtlinie
  • RSS-Anstieg nach fork(): Seitenkopien durch CoW
  • Vorhandenes Mapping mit MAP_FIXED überschrieben → MAP_FIXED_NOREPLACE empfohlen

Checkliste für die Praxis

  • Speicher sofort reservieren: mmap + PROT_READ|PROT_WRITE + MAP_PRIVATE|MAP_ANONYMOUS
  • Bei Codegenerierung: W^X einhalten, mprotect(PROT_READ|PROT_EXEC)
  • Bei Dateimappings: offset seitenaligned, kein Zugriff hinter EOF
  • Bei vielen Page Faults: MADV_WILLNEED oder vorab zugreifen
  • Speicheranalyse: /proc/<pid>/smaps_rollup/proc/<pid>/maps
  • fork großer Prozesse: CoW berücksichtigen, im Kind exec verwenden
  • In latenzkritischen Umgebungen: THP/mTHP, mlock und TLB-Verhalten beobachten

Noch keine Kommentare.

Noch keine Kommentare.