- 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
- Cache-Regeln behandeln
- Seitentabellen ändern
- 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
mmap → EINVAL: Fehler bei der Ausrichtung des Datei-Offsets
mmap → ENOMEM: 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.