Wie groß ist die Page‐Table?
Im vorigen (typischen) Beispiel verwenden wir 20 Bits zum
indizieren der Page‐Table. Typischerweise spendiert man 32 Bits pro Tabellen‐Zeile (im Vorigen Beispiel brauchten wir mindestens 18
Bits). Damit benötigen wir insgesamt:
Anzahl Page‐Table‐Einträge:
Größe der Page‐Table:
Wir benötigen so eine Page‐Table pro Prozess!
Noch gravierender ist es natürlich für 64‐Bit‐Adressen!
Größe der Page‐Table:
Techniken zur Reduktion der Page‐Table‐Größe
Page‐Table‐Größe ist limitiert durch ein spezielles Limit‐Register:
Adressen erst mal nur bis maximal dem Inhalt des Limit‐Registers erlaubt. Limit‐Register wird nur bei Bedarf (also überschreiten) erhöht. Sinnvoll, wenn Speicher nur in eine Richtung wächst.
Page‐Table ist in zwei Segmenten organisiert:
Beide Segmente wachsen wie vorhin beschrieben mittels eines Limit‐
Registers nur bei Bedarf. Ein Segment wird für den Stack verwendet und wächst von oben nach unten. Das andere Segment wird für den Heap verwendet und wächst von unten nach oben. Höchstes Adress‐
Bit bestimmt welches der beiden Segmente verwendet wird. (Also:
Speicher in zwei gleich große Teile unterteilt)
Techniken zur Reduktion der Page‐Table‐Größe
Invertierte Page‐Tables:
Es wird eine Hash‐Funktion auf die virtuelle Adresse angewendet.
Die Größe der Page‐Table entspricht der Anzahl Seiten im
physikalischen Speicher. Jeder Eintrag speichert die aktuellen High‐
Order‐Bits der Adressen zu den die aktuelle Page gehört.
Mehrere Level von Page‐Tables:
Oberster Level zeigt zunächst auf sehr große Blöcke (auch als
Segmente bezeichnet). Innerhalb eines Segments wird wiederum mittels Page‐Table feiner (dann als Pages bezeichnet) unterteilt.
Referenzieren einer Page: High‐Order‐Bits bestimmen das Segment (wenn vorhanden); die nächsten Bits dann die richtige Page in
diesem Segment. Nachteil dieses Verfahrens: Adress‐Translation ist aufwendiger.
Techniken zur Reduktion der Page‐Table‐Größe
Paged‐Page‐Tables:
Page‐Table befindet sich selber im virtuellen Speicher. Mögliche rekursive Page‐Faults müssen durch geeignete Betriebssystem‐
Mechanismen verhindert werden. (Keine weiteren Details hier)
Schreiben von Pages
Schreiben einer Page in den Swap‐Space ist sehr teuer (kostet millionen von CPU‐Zyklen).
Write‐Through‐Strategie (siehe Abschnitt über Caching) ist hier
somit nicht sinnvoll. Eine sinnvolle Strategie ist Write‐Back, d.h. nur, wenn die Seite von einer anderen in den Swap‐Space verdrängt
wird, wird diese auch in den Swap‐Space geschrieben.
Auch das ist immer noch gleich so teuer, kommt aber seltener vor.
Muss man eine verdrängte Seite eigentlich immer zurückschreiben?
Nur, wenn diese verändert wurde.
CPU muss bei jedem schreibenden Zugriff auf eine Page in der Page‐
Table ein Dirty‐Bit setzen.
Beobachtung für jeden Speicherzugriff
Bildquelle: David A. Patterson und John L. Hennessy, „Computer Organization and Design“, Fourth Edition, 2012
Virtueller Speicher ist aufwendiger als direkter physikalischer Zugriff
• Erst nachschlagen der Page im Speicher.
• Dann Zugriff auf den Speicher.
Wie kann man das soweit wie möglich beschleunigen?
Der Translation‐Lookaside‐Buffer (TLB)
Bildquelle: David A. Patterson und John L. Hennessy, „Computer Organization and Design“, Fourth Edition, 2012
Protection mittels virtuellem Speicher
Virtueller Speicher erlaubt, dass mehrere Prozesse auf denselben physikalischen Speicher zugreifen.
Es ist unter Umständen sinnvoll, den Speicherbereich vor Schreibzugriff zu schützen.
TLB und Page‐Table speichern ein extra RW‐Bit.
(1) Wer setzt dieses RW‐Bit? (2) Wie setzt man dieses Bit?
Zu (1): Ein Betriebssystem‐Prozess.
Zu (2): Einfache Maschineninstruktionen?
Problem: Jeder kann dann das Bit setzen.
Page
Prozess 1 Prozess 2
Betriebsmodi einer CPU
Häufig zwei unterschiedliche Betriebsmodi:
• Normaler Betriebsmodus
• Kernel‐ (oder auch Supervisor)‐Mode
CPU erlaubt die Ausführung bestimmter Maschinen‐Instruktionen nur im Kernel‐Mode.
Page‐Tables werden im physikalischen Speicher abgelegt, auf den kein anderer virtueller Adressraum zeigt.
Wie erreicht man den Kernel‐Mode? Es muss verhindert werden, dass jeder die CPU in diesen Modus versetzen kann.
Üblicher Weg: System‐Call.
Erinnerung: damit kann man eine Betriebssystemfunktion aufrufen.
Mit System‐Call wird eine Exception ausgelöst und an eine
Speicherstelle gesprungen, die nur in Kernel‐Mode zugreifbar ist.
Was passiert bei einem Page‐Fault noch?
Aktueller Prozess kann die Instruktion, die den Page‐Fault ausgelöst hat, nicht weiter ausführen.
Betriebssystem kann einem anderen Prozess die CPU zur Verfügung stellen.
Sobald die Page geladen ist, kann dem ursprünglichen Prozess die CPU wieder zur Verfügung gestellt werden.
Hierzu muss der ursprüngliche Prozesskontext wieder hergestellt werden; unter anderem natürlich der PC auf die Instruktion gesetzt werden, die den Page‐Fault verursacht hatte.
Randbemerkung: Segmentierung
Bisher haben wir feste Blockgrößen betrachtet. Es gibt auch ein Konzept mit variablen Blockgrößen: Segmentierung.
Adresse besteht aus Segment‐Nummer und Segment‐Offset:
Anfang kann auf einen beliebigen Startpunkt im Speicher zeigen.
Die physikalische Adresse ergibt sich aus Anfang + Offset.
Segmente können beliebig lang sein; benötigt auch „Bounds‐Check“.
Bildquelle: http://de.wikipedia.org/wiki/Segmentierung_(Speicherverwaltung)
Parallelität und Caches
Cache‐Kohärenz‐Problem
CPU 1 (oder Core 1) Cache
CPU 2 (oder Core 2) Cache
Speicher
Zeitschritt Event Cache‐Inhalt für CPU A
Cache‐Inhalt für CPU B
Speicherinhalt für Adresse X
0 0
1 CPU 1 liest X 0 0
2 CPU 2 liest X 0 0 0
3 CPU 1 speichert 1 nach X
1 0 1
Wann gilt ein Cache als kohärent?
1. Lesen von Speicherstelle X nach schreiben in Speicherstelle X sollte den geschriebenen Wert zurück geben, wenn kein
weiterer Prozess auf die Stelle X geschrieben hat.
2. Nachdem ein Prozess in Speicherstelle X geschrieben hat, sollte
„nach einer gewissen Zeit“ jeder Prozess den geschriebenen Wert in X vorfinden.
3. Zwei Schreiboperationen von zwei Prozessen in die
Speicherstelle X sollte von jedem Prozess in der gleichen Reihenfolge gesehen werden. (Schreiben ist serialisiert)
Wie erreicht man Kohärenz?
Write‐Invalidate‐Protokoll: Wenn ein Prozess in einen Speicherstelle schreibt wird die Speicherstelle in den Caches aller anderen Prozesse invalidiert.
Wie wird das Invalidieren technisch erreicht? Snooping: Jeder Cache‐Controller überwacht den gemeinsamen Bus, ob auf einen eigenen gecachten Inhalt
geschrieben wird.
Prozessor‐
aktivität
Busaktivität Inhalt des Caches von CPU A
Inhalt des Caches von CPU B
Speicher‐
inhalt für X 0
CPU A liest X Cache‐Miss für X 0 0
CPU B liest X Cache‐Miss für X 0 0 0
CPU A schreibt 1 nach X
Cache‐
Invalidierung für X
1 1
CPU B liest X Cache‐Miss für X 1 1 1