Speicherverwaltung (PI4)

Aus C und Assembler mit Raspberry
Version vom 17. Juli 2026, 07:00 Uhr von Satyria (Diskussion | Beiträge)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

Die Speicherverwaltung (Memory Management) ist das fundamentale Fundament jedes Betriebssystems. Sie entscheidet darüber, ob das OS stabil wie ein Fels in der Brandung läuft oder sich im reinsten Chaos (Page Faults, Speicherlecks, zerschossene Datenstrukturen) verabschiedet.

Hier ist eine strukturierte Übersicht darüber, was allgemein beachtet werden muss, wie sich der Betrieb mit und ohne MMU unterscheidet und warum kohärente Bereiche eine absolute Sonderrolle einnehmen.

Allgemeine Aspekte: Was gibt es zu beachten?

Bei der Entwicklung eines Kernels musst der Speicher in zwei völlig verschiedenen Dimensionen verwaltet werden:

  • Der Page Allocator (Seiten-Ebene):

Verwaltet grobe Speicherblöcke (meist 4 KB oder 64 KB). Er entscheidet, welche physischen Speicherseiten frei oder belegt sind. Hierbei ist Schnelligkeit Trumpf. Oft wird dafür eine einfache Freiliste (Free-List) oder ein Buddy-Allocator verwendet.

  • Der Heap Allocator (Byte-Ebene):

Teilt die groben Seiten in winzige, beliebige Häppchen für das Programm auf (das klassische `malloc`/`free`).

  • Das Problem der Fragmentierung:
    • Externe Fragmentierung: Es ist zwar genug Speicher frei, aber er ist in so viele winzige Lücken zerstückelt, dass kein großer zusammenhängender Block mehr allokiert werden kann.
    • Interne Fragmentierung: Ein Allokator gibt dir einen größeren Block, als du eigentlich angefordert hast (z. B. 64 Bytes für einen 10-Byte-String), wodurch der Rest ungenutzt verpufft.
  • Cache-Alignment (Ausrichtung):

CPUs lesen Daten am liebsten in "Cache-Lines" (meist 64 Bytes). Liegt eine Datenstruktur ungünstig über eine Cache-Line-Grenze hinweg, muss die CPU zwei statt einem Zugriff ausführen. DMA-Puffer *müssen* zwingend auf Cache-Line-Grenzen ausgerichtet sein.


Der MMU-Betrieb: Physisch vs. Virtuell

Die **MMU (Memory Management Unit)** ist der Hardware-Übersetzer deiner CPU. Sie entscheidet grundlegend darüber, wie dein System Speicher sieht.

Ohne MMU (Physische Adressierung / "Bare Metal")

Wie es läuft: Jede Adresse, die der Code anspricht, entspricht exakt der Leitung auf der Platine (Physische Adresse). Schreibt das Programm an `0x20000000`, landet es genau dort im echten RAM.

Vorteile: Extrem schnell (keine Übersetzungs-Latenz) und einfach zu debuggen.

Nachteile: Keine Sicherheit. Jedes Programm kann den Kernel-Speicher überschreiben. Zudem müssen alle Programme im selben physischen Speicherbereich koexistieren, was die Fragmentierung extrem verschlimmert.

Mit MMU (Virtuelle Adressierung)

Wie es läuft: Der Code arbeitet nur noch mit *virtuellen* Adressen. Die MMU übersetzt diese über mehrstufige Seitentabellen (Page Tables) transparent in physische Adressen.

Die Superkräfte der MMU:

Speicherschutz (Access Control): Man kann Speicherseiten als *Read-Only* (für Code) oder *Execute-Never (XN)* (für Daten, um Schadcode-Ausführung zu verhindern) markieren.

Virtueller Adressraum: Jedes User-Programm kann denken, es besitze den gesamten Speicher (z. B. ab Adresse `0x0`). Die MMU mappt das im Hintergrund auf völlig verschiedene physische Bereiche im RAM.

Caching-Attribute: Man kann im MAIR-Register (Memory Attribute Indirection Register) der CPU festlegen, welche Speicherbereiche rasant gecacht werden dürfen und welche direkt in die Hardware schreiben müssen.

Was ist an Coherent-Bereichen besonders?

Um zu verstehen, was Coherent (kohärente) Bereiche besonders macht, müssen wir uns das Problem moderner CPU-Caches ansehen.

CPUs sind um ein Vielfaches schneller als der physische RAM. Deshalb laden sie häufig genutzte Daten in den ultraschnellen L1/L2-Cache. Wenn die CPU eine Variable ändert, ändert sie diese oft erst nur im Cache (*Write-Back*) und schreibt sie erst viel später in den echten RAM.

 CPU Core <---> [ CPU-Cache ] (geänderte Daten)
                      |
                      x  <--- Hardware sieht die Änderung nicht!
                      v
 Hardware <---> [ Physischer RAM ] (alte Daten)

Das Problem mit DMA (Direct Memory Access)

Hardware-Peripherie (wie der PCIe-USB-Controller, die GPU oder eine Netzwerkkarte) hat keinen Zugriff auf die CPU-Caches. Sie liest und schreibt immer direkt im physischen RAM.

  • Schreibt die CPU Daten für den USB-Stick in einen normalen Speicherbereich, liegen sie im Cache. Der USB-Controller startet den DMA-Transfer, liest aber die veralteten Daten aus dem echten RAM. **Ergebnis: Datenkorruption.**
      1. Die Lösung: Der Coherent-Bereich

Ein kohärenter Speicherbereich (oft auch *Non-Cacheable* oder *Device-Memory* genannt) löst dieses Problem radikal, indem er das Caching komplett abschaltet.

  • **Kein Caching:** Jeder Lese- und Schreibzugriff der CPU geht unter Umgehung aller Caches direkt in den physischen RAM.


  • **Sofortige Sichtbarkeit:** Sobald die CPU ein Byte dorthin schreibt, kann eine Hardware-Komponente es sofort per DMA lesen (und umgekehrt).


  • **Der Preis:** Da die CPU nicht mehr auf den Cache zugreifen kann, sind Zugriffe auf kohärenten Speicher deutlich langsamer. Deshalb nutzt man sie ausschließlich für kleine Kommunikations-Puffer (wie Mailbox-Strukturen oder DMA-Deskriptoren) und niemals für Berechnungen oder normalen Programmcode.


---

Möchtest du als Nächstes tiefer in die konkrete Einrichtung der MMU-Seitentabellen für deinen Kernel einsteigen, um diese Bereiche sauber voneinander abzugrenzen?