12 KiB
Zusammenfassung: Speicherverwaltung
1. Einführung: Die Direkte Speicherverwaltung
Die einfachste und historisch älteste Form der Speicherverwaltung ist die direkte Verwaltung.
- Grundprinzip: Alle Adressen, die im Programmcode stehen, entsprechen direkt den physikalischen Adressen im Hauptspeicher (RAM). Es gibt keine Übersetzungsschicht.
- Beispiel: Der Assembler-Befehl
mov AX, 42bedeutet, dass der Prozessor direkt auf die physikalische Speicherzelle Nr. 42 zugreift und deren Inhalt in das Register AX lädt.
Historische Speichertypen
Frühere Computer unterschieden strikt zwei Arten von Speicher:
- ROM (Read-Only-Memory): Ein kleiner Speicher (ca. 64 KiB), der nicht überschrieben werden konnte. Er enthielt Boot-Funktionen, Treiber und Teile des Betriebssystems.
- RAM (Random-Access-Memory): Der große Arbeitsspeicher für das Betriebssystem und die Benutzerprogramme.
Organisation
Wenn nur ein einziges Benutzerprogramm und das Betriebssystem (OS) im Speicher sind, gab es drei Organisationsformen:
- OS im RAM (unten), Benutzerprogramm darüber.
- OS im ROM (oben), Benutzerprogramm im RAM.
- Treiber im ROM, OS und Benutzerprogramm im RAM.
Nachteile der direkten Verwaltung
Dieses Modell stößt schnell an Grenzen:
- Mangelnder Schutz: Ein Programm kann versehentlich das Betriebssystem oder andere Programme beschädigen, da es Zugriff auf jedes physikalische Byte hat.
- Kein echtes Multitasking: Es ist fast unmöglich, mehrere Programme gleichzeitig laufen zu lassen. Wenn zwei Programme hartkodierte Adressen nutzen (z.B. beide wollen an Adresse 1000 starten), können sie nicht gleichzeitig im Speicher liegen.
- Größenbeschränkung: Das gesamte Programm muss vollständig in den physikalischen Speicher passen ("The whole program must be presented in memory").
2. Die erste Lösung: Adressräume & Swapping
Um die Probleme der direkten Verwaltung zu lösen, wurden Adressräume eingeführt. Ein Adressraum ist die Menge aller Adressen, die ein Prozess verwenden darf. Jeder Prozess erhält seinen eigenen, isolierten Raum.
Hardware-Unterstützung (Basis & Limit)
Der Prozessor nutzt zwei spezielle Register zur Verwaltung:
- Basisregister: Enthält die physikalische Startadresse des Programms.
- Grenzwertregister (Limit): Enthält die Gesamtlänge des Programms.
- Berechnung: Die effektive physikalische Adresse = Programmadresse + Basisregister.
- Schutz: Die Hardware prüft bei jedem Zugriff: Effektive Adresse ≤ Basis + Limit. Ist dies nicht der Fall, wird der Zugriff verweigert (Schutzverletzung).
Das Konzept des Swapping (Auslagerung)
Da der Arbeitsspeicher selten groß genug ist, um alle Prozesse gleichzeitig vollständig zu halten, nutzt das OS "Swapping".
- Vorgang: Wenn der Platz knapp wird, wird ein inaktiver Prozess komplett auf die Festplatte geschrieben (ausgelagert) und ein anderer Prozess komplett in den Speicher geladen.
- Problem der Fragmentierung: Durch das ständige Ein- und Auslagern entstehen ungenutzte Lücken ("Löcher") zwischen den Prozessen. Der Speicher zerstückelt.
- Lösung: Das OS muss den Speicher defragmentieren (komprimieren), was jedoch sehr rechenaufwändig ist.
Speicherzuweisungs-Algorithmen (Allocation Algorithms)
Wenn ein neuer Prozess geladen werden soll, muss das OS eine passende Lücke im RAM finden. Hierfür gibt es fünf klassische Strategien:
- First Fit: Wählt die erste Lücke, die groß genug ist. Schnell, aber fragmentiert den Speicher vorne.
- Next Fit: Beginnt die Suche dort, wo die letzte Zuweisung endete.
- Best Fit: Sucht die kleinste Lücke, die gerade so ausreicht. Nachteil: Hinterlässt winzige, nutzlose Rest-Lücken.
- Worst Fit: Wählt die größte verfügbare Lücke. Idee: Der Rest ist groß genug für weitere Prozesse.
- Quick Fit: Hält separate Listen für häufig benötigte Lückengrößen bereit.
3. Virtuelle Speicherverwaltung (Paging)
Das ist der Standard in allen modernen Systemen (Windows, Linux, Android, macOS). Es entkoppelt die Sicht des Programms von der physikalischen Realität.
Kernkonzepte
- Virtueller Speicher: Das Programm "sieht" einen perfekten, linearen Speicher, der in Pages (Seiten) unterteilt ist.
- Physischer Speicher: Der RAM ist in Frames (Seitenrahmen) unterteilt.
- Mapping: Es gilt immer: Größe Frame = Größe Page (Standard ist oft 4 KiB).
- Vorteil: Ein Programm kann größer sein als der physikalische Speicher. Die Seiten müssen nicht zusammenhängend im RAM liegen.
MMU (Memory Management Unit)
Die MMU ist ein Hardware-Baustein direkt auf dem Prozessor-Chip.
- Aufgabe: Sie übersetzt bei jedem Speicherzugriff die virtuelle Adresse in eine physikalische Adresse.
- Richtung: Immer Virtuell → Physikalisch (niemals umgekehrt!).
Berechnung der optimalen Seitengröße
Die Wahl der Seitengröße ist ein Kompromiss.
- Kleine Seiten: Weniger Verschnitt (interne Fragmentierung), aber riesige Verwaltungstabellen.
- Große Seiten: Kleine Tabellen, aber viel ungenutzter Platz am Ende der letzten Seite.
- Formel: p = √(2 · s · e)
- p: Seitengröße
- s: Durchschnittliche Prozessgröße
- e: Größe eines Seitentabellen-Eintrags
- Beispiel: Bei 1 MiB Programmgröße und 8 Byte Eintrag ergibt sich mathematisch eine optimale Größe von 4 KiB.
4. Datenstrukturen: Seitentabellen & TLB
Die Seitentabelle (Page Table)
Jeder Prozess besitzt eine eigene Seitentabelle im RAM. Sie dient der MMU als Nachschlagewerk. Ein Eintrag in dieser Tabelle (typisch 32 Bit / 4 Byte) enthält komplexe Statusinformationen:
- Rahmennummer: Die eigentliche physikalische Adresse im RAM (wo liegt die Seite?).
- Present/Absent Bit (P):
- 1: Seite ist im RAM (Zugriff okay).
- 0: Seite ist nicht im RAM (löst Seitenfehler / Page Fault aus).
- Modified Bit (M) / Dirty Bit:
- 1: Die Seite wurde verändert (geschrieben). Sie muss beim Entfernen auf die Festplatte zurückgesichert werden.
- 0: Seite ist sauber (identisch mit der Festplatte), kann einfach überschrieben werden.
- Referenced Bit (R):
- 1: Seite wurde kürzlich gelesen oder beschrieben. Wichtig für Ersetzungs-Algorithmen. Das OS setzt dieses Bit periodisch auf 0 zurück.
- Schutz-Bits (RWX): Definieren Rechte (Read, Write, Execute).
Das Problem der Größe
Da jeder Prozess eine Tabelle braucht, kann dies viel Speicher kosten. Bei 32-Bit oder 64-Bit Systemen können Tabellen mehrere Megabyte groß werden (z.B. 4 MiB).
- Lösung: Mehrstufige Seitentabellen (Paged Page Tables).
TLB (Translation Lookaside Buffer)
Da ein Zugriff auf die Seitentabelle im RAM langsam ist (Verdopplung der Zugriffszeit: 1x Tabelle lesen, 1x Daten lesen), nutzt die MMU einen Hardware-Cache namens TLB.
- Der TLB speichert die häufigsten Übersetzungen.
- Die MMU prüft zuerst den TLB. Nur bei einem "Miss" greift sie auf die langsame Tabelle im RAM zu.
5. Seitenersetzungs-Algorithmen (Page Replacement)
Wenn der physikalische Speicher voll ist und eine neue Seite geladen werden muss (Demand Paging), muss eine alte Seite weichen ("Opfer"). Welcher Algorithmus trifft diese Entscheidung?
A. NRU (Not Recently Used)
Klassifiziert Seiten anhand der R- und M-Bits in 4 Klassen:
- Klasse 0: Nicht referenziert, nicht modifiziert. (Bestes Opfer).
- Klasse 1: Nicht referenziert, modifiziert.
- Klasse 2: Referenziert, nicht modifiziert.
- Klasse 3: Referenziert, modifiziert. (Schlechtes Opfer, wird oft gebraucht). Strategie: Löscht zufällig eine Seite aus der niedrigsten vorhandenen Klasse. Einfach, aber mäßig effizient.
B. FIFO (First-In-First-Out)
- Strategie: Führt eine Liste aller Seiten. Die älteste (zuerst geladene) Seite fliegt raus.
- Nachteil: Die älteste Seite kann eine wichtige Kernel-Bibliothek sein, die ständig benötigt wird. FIFO ist daher in Reinform ineffizient.
C. Second Chance (SC)
Eine Verbesserung von FIFO. Prüft das R-Bit der ältesten Seite:
- R=0: Seite ist alt und unbenutzt → Raus.
- R=1: Seite ist alt, aber aktiv → R-Bit löschen, Seite ans Ende der Liste verschieben (wie neu geladen). Sie erhält eine "zweite Chance".
D. Clock (Uhr-Algorithmus)
- Eine effizientere Implementierung von Second Chance mittels einer zirkulären Liste (wie ein Zifferblatt).
- Ein Zeiger dreht sich im Kreis. Trifft er auf R=1, setzt er es auf 0 und geht weiter. Trifft er auf R=0, wird die Seite ersetzt.
E. LRU (Least Recently Used)
- Idee: Die Vergangenheit sagt die Zukunft voraus. Seiten, die lange nicht genutzt wurden, werden wohl auch bald nicht gebraucht.
- Implementierung: Theoretisch bräuchte man einen Zeitstempel bei jedem Zugriff (zu teuer).
- Praxis: Hardware-Zähler in der Seitentabelle oder Matrix-Methoden. Gilt als einer der besten Algorithmen.
F. Working Set (WS)
- Definiert eine "Arbeitsmenge" an Seiten, die ein Prozess in einem Zeitfenster τ (z.B. 100ms) benötigt hat.
- Strategie: Entferne nur Seiten, die nicht zum aktuellen Working Set gehören. Verhindert "Thrashing" (das System ist nur noch mit Auslagern beschäftigt).
6. Der Ablauf eines Seitenfehlers (Page Fault)
Was passiert im Detail, wenn ein Prozess auf eine Adresse zugreift, deren Present-Bit 0 ist? Dies ist ein komplexer Vorgang in 11 Schritten:
- Trap: Die Hardware bemerkt den Fehler. Ein Interrupt wird ausgelöst, der Programmzähler (PC) wird im Stack gesichert. Der Modus wechselt zu Kernel-Mode.
- Sichern: Alle flüchtigen Informationen (Register) werden gespeichert.
- Identifikation: Das OS ermittelt, welche virtuelle Adresse den Fehler verursacht hat (steht oft in einem Spezialregister der CPU).
- Validierung: Das OS prüft: Ist die Adresse überhaupt gültig? Hat der Prozess Rechte dafür? Falls nein → Prozess-Abbruch (Segmentation Fault).
- Rahmensuche: Das OS sucht einen freien physikalischen Rahmen. Falls keiner frei ist, muss der Seitenersetzungsalgorithmus (siehe Punkt 5) ausgeführt werden, um Platz zu schaffen.
- Auslagern (Eviction): Wenn die "Opfer-Seite" modifiziert war (M=1), muss sie erst auf die Festplatte geschrieben werden.
- Kontextwechsel: Da Festplattenzugriffe langsam sind, wird der aktuelle Prozess blockiert und ein anderer Prozess darf solange rechnen (CPU-Nutzung optimieren).
- Laden: Die eigentlich angeforderte Seite wird nun von der Festplatte in den freien Rahmen geladen.
- Update: Die Seitentabelle wird aktualisiert: Rahmennummer eintragen, Present-Bit auf 1 setzen.
- Wiederherstellung (Restore): Die gesicherten Register des Prozesses werden wiederhergestellt. Der Programmzähler wird auf den Befehl zurückgesetzt, der den Fehler verursachte.
- Fortsetzung: Der Befehl wird erneut ausgeführt – diesmal erfolgreich, da die Seite nun im RAM ist.
7. Segmente & Speicherschutz
Ein moderner Prozess-Adressraum ist nicht homogen, sondern in logische Segmente unterteilt, die unterschiedliche Rechte (Schutz-Bits) haben:
- Code-Segment (Text): Enthält die Maschinenbefehle.
- Rechte: Nur Lesen und Ausführen (r-x). Schreiben ist verboten (verhindert selbst-modifizierenden Code).
- Daten-Segment: Enthält globale Variablen.
- Rechte: Lesen und Schreiben (rw-). Ausführen verboten (Schutz vor Buffer-Overflow-Exploits).
- Stack-Segment: Für lokale Variablen und Funktionsaufrufe. (rw-).
- Heap-Segment: Für dynamisch angeforderten Speicher (malloc / new). (rw-).
Segmentation Fault
Ein "Segfault" tritt auf, wenn ein Programm versucht, auf eine Adresse zuzugreifen, für die es keine Rechte hat (z.B. Schreiben in Code-Segment) oder die nicht existiert (z.B. Dereferenzierung eines Null-Zeigers).
8. Vergleich: Swapping vs. Paging
| Merkmal | Swapping (Veraltet) | Paging (Modern) |
|---|---|---|
| Einheit | Ganzer Prozess wird verschoben. | Einzelne Seiten (Teile) werden verschoben. |
| Speicherart | Nur bei direkter Speicherverwaltung nötig. | Nur bei virtueller Speicherverwaltung. |
| Effizienz | Gering (große Datenmengen, Fragmentierung). | Hoch (feingranular, nur benötigte Teile). |
| Hardware | Benötigt Basis-/Limit-Register. | Benötigt MMU und Seitentabellen. |
Demand Paging
Moderne Systeme laden Seiten nicht im Voraus ("Prefetching"), sondern strikt nach Bedarf ("Demand"). Eine Seite wird erst geladen, wenn ein Page Fault auftritt. Das spart Speicher und I/O-Bandbreite.