Virtueller Speicher Einführung
Virtueller Speicher Einführung
Virtueller Speicher Einführung
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
Vorwort:<br />
<strong>Virtueller</strong> <strong>Speicher</strong><br />
Die virtuelle <strong>Speicher</strong>technik besitzt ein sehr breites Spektrum an verschiedenen<br />
Realisierungen. Es gibt praktisch keine zwei Architekturen, welche genau die gleiche<br />
Implementierung der virtuellen <strong>Speicher</strong>technik besitzen. Es würde deshalb den Rahmen<br />
sprengen auf jedes Detail einzugehen. Es sollen deshalb vielmehr die Grundideen und<br />
Prinzipien der virtuellen <strong>Speicher</strong>technik vermittelt werden.<br />
• Inhalt<br />
• <strong>Einführung</strong><br />
o Motivation<br />
• Allgemeine Prinzipien<br />
o Prinzip der Lokalität<br />
o <strong>Speicher</strong>hierarchie<br />
• Adreßumsetzung<br />
o Mapping<br />
o Seitentafel (page table)<br />
Mehrstufige Seitentafel<br />
Invertierte Seitentafel<br />
• Seitenverwaltung<br />
o Blockplazierung<br />
o Blockersetzung<br />
o Schreibstrategie<br />
o Seitenfehler<br />
o Seitengröße<br />
o Seitengrößenerweiterung (page size extension)<br />
• Adreßumsetzungspuffer (TLB)<br />
• <strong>Speicher</strong>schutz<br />
• Zusammenfassung<br />
• Literatur<br />
• Über dieses Dokument ...<br />
<strong>Einführung</strong><br />
1962 hatte Kilburn die Idee, die zwei Ebenen Kern- und Trommelspeicher automatisch zu<br />
verwalten und als eine Ebene erscheinen zu lassen. Er demonstrierte seine Idee eines<br />
virtuellen <strong>Speicher</strong>s an einem Atlas-Computer an der University of Manchester [1].
Zuerst wurde die virtuelle <strong>Speicher</strong>technik nur in Großrechnern implementiert. Erst seit<br />
Entwicklung des 386-Prozessor (interne MMU ) findet sie auch in der Welt des PCs<br />
Verwendung. Seit über 10 Jahren ist die virtuelle <strong>Speicher</strong>technik in jeder Workstation<br />
implementiert. Es gibt aber spezielle Systeme (Embedded Systems) die keinen virtuellen<br />
<strong>Speicher</strong> benutzen, da sie besonders zeiteffektive Serverfunktionen durchführen.<br />
Die MMU (memory management unit) ist ein Bestandteil von Mikroprozessoren, der<br />
die internen Register und <strong>Speicher</strong>bereiche wie z.B. virtuellen <strong>Speicher</strong> und<br />
Prozessor-Caches steuert.<br />
.<br />
Motivation<br />
Der Wunsch von Programmierern ist es, über einen scheinbar unbeschränkt großen <strong>Speicher</strong><br />
zu verfügen, um nicht darauf achten zu müssen, daß die Programme zu groß werden.<br />
Deswegen benötigt man eine <strong>Speicher</strong>technik, welche mehr Code und Daten im <strong>Speicher</strong><br />
unterbringen kann als realer <strong>Speicher</strong> (Hauptspeicher) vorhanden ist.<br />
Ein weiteres Problem der <strong>Speicher</strong>verwaltung ist die Fragmentierung des realen <strong>Speicher</strong>s, da<br />
Code und Daten eines Prozesses fortlaufende Adressen haben müssen. Dies kann dazu führen,<br />
daß für einen Prozeß kein Platz mehr frei ist, obwohl noch an anderer Stelle Platz im <strong>Speicher</strong><br />
frei ist. Dies wird in Abb. 1.1 dargestellt.<br />
Abbildung 1.1: Ohne virtuellen <strong>Speicher</strong> hat Prozeß 2 zuwenig Platz, um noch weitere Seiten<br />
zu benutzen. Mit virtuellem <strong>Speicher</strong> kann er Platz an einer anderen freien Stelle im<br />
Hauptspeicher verwenden.<br />
In den heutigen Multiprocessing-Betriebssystemen laufen mehrere Prozesse gleichzeitig.<br />
Dabei ist ein Schutz von Daten und Code der einzelnen Prozesse nötig, sowie die Möglichkeit<br />
der Kommunikation zwischen den Prozessen und der gemeinsamen Nutzung von Code und
Daten. Dazu muß der physikalische <strong>Speicher</strong> auch auf mehrere Prozesse aufgeteilt werden<br />
können, um sinnvoll mit Multiprocessing zu arbeiten. Die virtuelle <strong>Speicher</strong>technik löst diese<br />
Probleme, indem sie die Ebenen Hauptspeicher und sekundären <strong>Speicher</strong> automatisch und<br />
geschickt steuert.<br />
Prinzip der Lokalität<br />
Die meisten Programme greifen nicht gleichmäßig auf ihren Code und ihre Daten zu [2]. Die<br />
Untersuchung dieser Feststellung führt zum Prinzip der Lokalität, daß sich in zeitliche und<br />
räumliche Lokalität aufteilt.<br />
• räumliche Lokalität: Nachdem auf eine Adresse zugegriffen wurde, erfolgt<br />
wahrscheinlich ein Zugriff auf eine benachbarte Adresse.<br />
Das linke Beispiel in Abb. 2.1 zeigt die räumliche Lokalität anhand eines<br />
Codestückes. Der lineare Verlauf stellt die normale Programmabarbeitung dar, bei der<br />
der Programmzähler laufend incrementiert wird. Die Wiederholung deutet auf eine<br />
Schleife hin, die vier mal durchlaufen wird. Danach gibt es eine Sprunganweisung und<br />
das Programm wird weiter abgearbeitet.<br />
• zeitliche Lokalität: Nachdem auf eine Adresse zugegriffen wurde, erfolgt<br />
wahrscheinlich bald ein weiterer Zugriff auf dieselbe Adresse.<br />
Das rechte Beispiel von Abb. 2.1 stellt die zeitliche Lokalität dar. Die Quadrate<br />
könnten z.B. Variablen sein. Eine Variable steht immer an der gleichen Adresse, die<br />
Adresse wird aber öfters benutzt um auf den Wert der Variablen zuzugreifen oder um<br />
an der Adresse einen neuen Wert zu speichern.<br />
Abbildung 2.1: Beispiele für räumliche und zeitliche Lokalität.<br />
Das Prinzip der Lokalität ist von großer Bedeutung beim Entwurf einer <strong>Speicher</strong>hierarchie. Es<br />
führt zum Konzept einer auf unterschiedlichen Geschwindigkeiten und Kapazitäten<br />
basierenden Hierarchie.<br />
<strong>Speicher</strong>hierarchie
Eine <strong>Speicher</strong>hierarchie ist eine Organisationsform von <strong>Speicher</strong>n. Die verschiedenen<br />
<strong>Speicher</strong>arten werden in Ebenen aufgeteilt (z.B. Register, Cache, Hauptspeicher und<br />
Festplatte). Jede Ebene ist schneller, kleiner und teurer (Preis pro Byte) als die Ebene darunter<br />
(vgl. Abb. 2.1). Alle Daten einer Ebene sind auch in der Ebene darunter enthalten. Die<br />
kleinste Informationseinheit in einer <strong>Speicher</strong>hierarchie nennt man Block. Zwischen den<br />
Ebenen werden immer nur komplette Blöcke verschoben. Einen Erfolg eines Zugriffs in einer<br />
Ebene nennt man Treffer (hit), einen Mißerfolg Fehlzugriff (miss) (Block nicht in der Ebene).<br />
Die Trefferrate (hit rate) ist der prozentuale Anteil der Treffer an den Zugriffen. Der<br />
prozentuale Anteil der Fehlzugriffe nennt man Fehlzugriffsrate. Das Ziel einer<br />
<strong>Speicher</strong>hierarchie ist es, mit dem großen <strong>Speicher</strong>bereich einer unteren Ebene zu arbeiten<br />
und dieses mit der Geschwindigkeit einer oberen Ebene.<br />
Abbildung 2.2: <strong>Speicher</strong>hierarchie<br />
Mapping<br />
Das Prinzip der virtuellen <strong>Speicher</strong>technik ist die Trennung des Adreßraumes des<br />
Hauptspeichers vom Adreßraum, den die Prozesse benutzen (virtueller Adreßraum). Dabei<br />
kann jeder Prozeß seinen eigenen virtuellen Adreßraum benutzen. Sowohl der virtuelle<br />
<strong>Speicher</strong> als auch der reale <strong>Speicher</strong> werden in Blöcke gleicher Größe aufgeteilt. Beim<br />
virtuellen <strong>Speicher</strong> nennt man die Blöcke Seiten (pages), beim Hauptspeicher Rahmen<br />
(frames). Die Seiten des virtuellen <strong>Speicher</strong>s werden auf die Rahmen im Hauptspeicher<br />
abgebildet (mapping). Dabei ist es egal, auf welchen Rahmen, man kann irgendeinen freien<br />
Rahmen wählen (vgl. Abb. 3.1).
Abbildung 3.1: Abbildung (Mapping) der virtuellen Adreßräume in den Hauptspeicher.<br />
Deshalb kann der Hauptspeicher besser ausgenutzt werden, die Prozesse müssen nicht am<br />
Stück im Hauptspeicher liegen und das Problem der Fragmentierung des Hauptspeichers ist<br />
somit beseitigt. Es kann auch Seiten geben, die auf keinen Rahmen abgebildet werden. Diese<br />
Seiten liegen dann nur auf einem externen <strong>Speicher</strong> (Festplatte). Dies wird ausgenutzt um<br />
einen wesentlich größeren <strong>Speicher</strong>bereich verwenden zu können als Hauptspeicher<br />
vorhanden ist. Wurde früher ein Programm zu groß für den Hauptspeicher war es das Problem<br />
des Programmierers, es irgendwie passend zu machen. Der virtuelle Adreßraum ist nicht nur<br />
größer als der physikalische Adreßraum, sondern kann auch Lücken besitzen (vgl. Abb.3.2).<br />
Die virtuelle <strong>Speicher</strong>technik reduziert auch meistens die Startzeit eines Prozesses, da nicht<br />
der gesamte Code in den Hauptspeicher geladen werden muß, bevor der Prozeß starten kann.<br />
Abbildung 3.2: <strong>Virtueller</strong> <strong>Speicher</strong> > Hauptspeicher<br />
Über die Abbildungsvorschrift läßt sich die Position einer Seite im Hauptspeicher finden. Was<br />
aber wenn man auf eine bestimmte Adresse innerhalb einer Seite zugreifen will? Dazu wird<br />
eine virtuelle Adresse in zwei Hälften unterteilt, der werthöheren Hälfte, der Seitennummer<br />
und der wertniederen Hälfte, dem Offset. Die physikalische Adresse besteht entsprechend aus<br />
Rahmennummer und Offset (vgl. Abb. 3.3). Dabei ist die Seitennummer die Adresse einer<br />
einzelnen Seite im virtuellen <strong>Speicher</strong>. Der Offset ist die Adresse innerhalb einer Seite. Die<br />
Länge des Offsets ist abhängig von der Seitengröße, sie ist genau log2(Seitengröße). Die<br />
Länge der virtuellen Adresse ist architekturabhängig. Sie beträgt z.Z. meistens 32 Bit. Bei der<br />
Länge der physikalischen Adresse kommt es auf den <strong>Speicher</strong>ausbau an. Da der<br />
Hauptspeicher die gleiche Blockgröße wie der virtuelle <strong>Speicher</strong> hat, kann bei der<br />
Adreßumsetzung der Offset der virtuellen Adresse einfach an die Rahmennummer angehängt<br />
werden. Es muß also nur die Seitennummer in die entsprechende Rahmennummer übersetzt<br />
werden. Die Abbildungsvorschrift wird in Form einer Seitentafel implementiert.
Abbildung 3.3: Umwandlung von virtueller in physikalische Adresse. Die Seiten- bzw.<br />
Rahmengröße beträgt 2 12 = 4 KB. Die Anzahl der Rahmen beträgt 2 18 (= 1GB <strong>Speicher</strong>platz)<br />
und die Anzahl der Seiten beträgt 2 20 (= 4 GB <strong>Speicher</strong>platz).<br />
Seitentafel (page table)<br />
Eine Seitentafel oder Seitentabelle ist eine Datenstruktur, die durch die Seitennummern<br />
indexiert wird, d.h. jede Seite muß einen Eintrag in der Seitentafel haben. Die Anfangsadresse<br />
der Seitentafel steht in einem Register, dem Seitentafelregister. Jeder Eintrag der Seitentafel<br />
enthält die Rahmennummer der zugehörigen Seitennummer, falls diese im realen <strong>Speicher</strong> ist,<br />
und eine Verwaltungseinheit mit verschiedenen Steuerbits. Jeder einzelne Eintrag hat eine<br />
Länge von 32 Bit. Durch ein Präsenzbit (Valid-Bit) wird angezeigt, ob sich eine Seite im<br />
Hautspeicher befindet (Valid-Bit gesetzt) oder nicht (Valid-Bit nicht gesetzt). Die Größe der<br />
Seitentafel ist von der Anzahl der Seiten im virtuellen Adreßraum abhängig, z.B. eine<br />
Seitentafel mit einem 32-Bit Adreßraum, 4 KB großen Seiten und 4 Byte pro Eintrag, hat eine<br />
Größe von 4 MB (2 20 x4 Byte). Hat man nun eine virtuelle Adresse, so nimmt man die<br />
Seitennummer und greift auf den entsprechenden Eintrag in der Seitentafel zu (z.B.<br />
Seitenummer 3210 würde ein Zugriff auf den 3210. Eintrag in der Seitentafel bedeuten). Falls<br />
sich die Seite im Hauptspeicher befindet, steht in diesem Eintrag die dazugehörige<br />
Rahmennummer. An diese wird dann der Offset der virtuellen Adresse angehängt, dann kann<br />
mit der entstandenen physikalischen Adresse auf den Hauptspeicher zugegriffen werden. Der<br />
ganze Vorgang ist in Abb. 3.4 graphisch dargestellt.
Abbildung 3.4: Adreßumsetzung mit Seitentafel<br />
Mehrstufige Seitentafel<br />
Es wäre verschwenderisch, so große Seitentafeln die ganze Zeit im Hauptspeicher zu halten.<br />
Deswegen wird meistens eine mehrstufige Seitentafel verwendet, wobei die Seitennummer in<br />
mehrere Teile zerlegt wird. Alle Teile, bis auf den untersten Teil, werden für den Zugriff auf<br />
Seitentafeln verwendet, welche keine Rahmennummer im Eintrag haben, sondern einen<br />
Pointer auf eine andere Seitentafel. Mit dem höchsten Teil der Seitennummer wird ein<br />
Seitentafeleintrag indexiert, dessen Pointer auf eine weitere Seitentafel zeigt. Mit dem<br />
nächsten Teil der Seitennummer wird dann diese Seitentafel indexiert, wodurch man wieder<br />
einen Eintrag mit einem Pointer auf eine andere Seitentafel erhält. Dies wird solange<br />
fortgesetzt, bis man am untersten Teil der Seitennummer angekommen ist. Dieser indexiert<br />
dann einen Seitentafeleintrag mit einer Rahmennummer anstatt einem Pointer. An diese<br />
Rahmennummer muß jetzt nur noch der Offset angehängt werden, um die physikalische<br />
Adresse zu erhalten.<br />
In Abb. 3.5 wird eine 20-Bit Seitennummer in 2x10 Bit aufgeteilt. Mit den oberen 10 Bit wird<br />
auf die Seitentafel, auf die das Seitentafelregister zeigt, zugegriffen. Ist im entsprechenden<br />
Eintrag der Seitentafel ein Nullpointer, so befindet sich der Adreßbereich nicht in Benutzung.<br />
Ansonsten gibt es in dem Eintrag einen Pointer auf eine normale Seitentafel, auf welche mit<br />
den unteren 10 Bit zugegriffen wird. Der indexierte Eintrag enthält dann, falls das Valid-Bit<br />
gesetzt ist, die Rahmennummer, ansonsten gibt es einen Seitenfehler. Die mehrstufige<br />
Seitentafel hat den Vorteil, daß alle Seitentafeln selber wieder in 4 KB große Seiten passen (4<br />
Byte x 2 10 Einträge, bei 4 Byte großen Einträgen) und damit auf externen <strong>Speicher</strong><br />
ausgelagert werden können. Die Seitentafel mit den Pointern bleibt natürlich im<br />
Hauptspeicher.
Abbildung 3.5: Mehrstufige Seitentafel<br />
Invertierte Seitentafel<br />
Eine andere Möglichkeit, die Größe der Seitentafel zu reduzieren, ist die Benutzung einer<br />
invertierten Seitentafel (inverted page table). Die Idee dabei ist es, einen Eintrag pro Rahmen<br />
anstatt einen pro Seite in der Seitentafel zu verwenden. Dazu benötigt man eine Hash-<br />
Funktion, die die virtuellen Adressen auf die Rahmen aufteilt. Die Größe der Seitentafel ist<br />
jetzt nur noch von der Zahl der Rahmen im Hauptspeicher abhängig, welche normal<br />
wesentlich kleiner ist als die Anzahl der virtuellen Seiten, so daß man meist die ganze<br />
Seitentafel im Hauptspeicher halten kann. Ein Beispiel einer Architektur, die eine invertierte<br />
Seitentafel verwendet, ist der PowerPC (IBM/Motorola).<br />
Blockplazierung<br />
Die virtuelle <strong>Speicher</strong>technik hat sehr hohe Zugriffszeiten bei Seitenfehlern, da die Seite nicht<br />
im Hauptspeicher ist und ein Zugriff auf Festplatte erfolgen muß (vgl. Tabelle 4.1). Deshalb<br />
versucht das Betriebssystem die Blöcke möglichst geschickt zu plazieren, um weniger<br />
Seitenfehler zu verursachen. Dazu nimmt man auch kompliziertere Plazierungsalgorithmen in<br />
Kauf. Man entscheidet sich deswegen für vollassoziative Blockplazierung, bei der die Blöcke<br />
irgendwo im Hauptspeicher plaziert werden können.
Blockersetzung<br />
Wenn alle Rahmen des Hauptspeichers belegt sind und eine neue Seite eingeräumt werden<br />
soll, muß irgendeine Seite auf den externen <strong>Speicher</strong> ausgeräumt werden. Um zu bestimmen,<br />
welcher Block ersetzt wird, gibt es folgende Ersetzungsalgorithmen:<br />
• Beim FIFO-Algorithmus (first in first out) wird der zuerst eingespeicherte Block<br />
ersetzt. Es gibt viele Beispiele, bei denen die Anwendung des FIFO-Algorithmuses<br />
schlecht ist, z.B. bei großen Schleifen kann es passieren, daß der Algorithmus die<br />
Adressen des Beginns der Schleife am Schluß der Schleife wieder austauscht.<br />
Deswegen wird er nur selten implementiert.<br />
• Beim LIFO-Algorithmus (last in first out) wird der zuletzt eingespeicherte Block<br />
ersetzt. Der Algorithmus wiederspricht dem Prinzip der Lokalität und wird nicht<br />
benutzt.<br />
• Beim LRU-Algorithmus (least recently used) wird der am längsten ungenutzte Block<br />
ersetzt. Der LRU-Algorithmus liefert sehr gute Ergebnisse, braucht aber eine<br />
umfangreiche Verwaltung, falls er explizit den am längsten unbenutzten Block<br />
bestimmen soll.<br />
• Beim LFU-Algorithmus (least frequently used) wird der am wenigsten benutze Block<br />
ersetzt. Der LFU-Algorithmus ist sehr ähnlich wie der LRU-Algorithmus. Auch er<br />
liefert sehr gute Ergebnisse, braucht aber auch eine umfangreiche Verwaltung, falls er<br />
explizit den am wenigsten benutzten Block bestimmen soll. Deswegen wird meist ein<br />
vereinfachter Algorithmus verwendet, der nur wenige Zustände zwischen oft und<br />
wenig benutzten Seiten hat.<br />
• Beim Random-Algorithmus wird ein zufällig gewählter Block ersetzt. Es kann dabei<br />
im schlechtesten Fall auch gerade der Block ersetzt werden, den man gerade<br />
verwendet hat. Im Mittel liefert er doch ein gutes Ergebnis und ist recht einfach zu<br />
implementieren.<br />
Besonders effektiv arbeitet das Betriebssystem, wenn mehrere Blöcke auf einmal ersetzt<br />
werden, da der E/A Aufwand bei größeren Schreib- bzw. Lesevorgängen im Verhältnis zu<br />
mehreren kleinen günstiger ist.<br />
Fast immer benutzen die Betriebssysteme eine Approximation des LRU-Algorithmus. Man<br />
benutzt Use-Bits in der Verwaltungseinheit der Seitentafel, welche beim Seitenzugriff gesetzt<br />
und periodisch wieder gelöscht werden. Es kann dann zu einer bestimmten Zeit gesagt<br />
werden, ob auf eine Seite innerhalb einer Periode zugegriffen wurde. Aus allen Seiten mit<br />
ungesetztem Use-Bit wird dann eine ausgewählt, die ersetzt wird. Am günstigsten ist immer<br />
eine Seite mit ungesetztem Dirty-Bit zu ersetzen, weil diese nicht zurückgeschrieben werden<br />
muß. Da bei diesem vereinfachten Algorithmus die am seltesten genutzen Seiten den am<br />
längsten nicht genutzten Seiten entsprechen, ist diese Methode gleichzeitig eine Art<br />
vereinfachter LFU-Algorithmus.<br />
Schreibstrategie<br />
• falls alle Seiten auch im externen <strong>Speicher</strong> sind<br />
Das Schreiben auf externen <strong>Speicher</strong> benötigt hunderttausende Taktzyklen, deswegen<br />
wäre es viel zu langsam, alle Änderungen direkt auf Platte zu schreiben. Die
Schreibstrategie ist also write-back, d.h. die Seiten werden erst bei Ersetzung auf den<br />
externen <strong>Speicher</strong> zurückgeschrieben. Um nur die Blöcke zurückzuschreiben, die<br />
verändert wurden, wird ein Dirty-Bit in der Verwaltungseinheit der Seitentafel<br />
benutzt. Ist es nicht gesetzt, wurde die Seite nicht geändert und die Seite muß nicht<br />
einmal zurückgeschrieben werden, wenn sie ersetzt wird. Ist das Dirty-Bit gesetzt,<br />
wurde die Seite verändert und ist bei der Ersetzung zurückzuschreiben.<br />
• falls nur Seiten die nicht im Hauptspeicher sind im externen <strong>Speicher</strong> sind<br />
Hierbei werden die Seiten einfach getauscht. Wenn eine Seite in den Hauptspeicher<br />
eingeräumt werden soll, wird einfach eine nach einem Ersetzungsalgorithmus<br />
ausgewählte Seite vom Hauptspeicher in den externen <strong>Speicher</strong> ausgeräumt und dann<br />
die gewünschte Seite in den Hauptspeicher eingeräumt. Diese befindet sich danach<br />
aber nicht mehr auf externem <strong>Speicher</strong>. Die Methode ist ein wenig langsamer, aber sie<br />
hat einen größeren virtuellen <strong>Speicher</strong>bereich; z.B. bei 64 MB Hauptspeicher und 128<br />
MB externen <strong>Speicher</strong> hat sie 196 MB virtuellen <strong>Speicher</strong> anstatt 128 MB bei der<br />
oberen Methode. Außerdem kann die Methode nahezu gleich schnell gemacht werden,<br />
indem darauf geachtet wird, daß man, sobald die Anzahl freier Seiten unter eine<br />
bestimmte untere Schranke sinkt, wieder freie Seiten schafft, bis eine bestimmte obere<br />
Schranke an freien Seiten erreicht wird.<br />
Seitenfehler<br />
Wird auf einen Eintrag in der Seitentafel zugegriffen, dessen Valid-Bit auf null ist, wird eine<br />
Ausnahme geworfen. Der Seitenüberwacher, ein Teil des Betriebssystems, der für das<br />
Seitenmanagement verantwortlich ist, muß die Seite nun in den Hauptspeicher holen. Die<br />
Auswahl der zu ersetzenden Seite erfolgt mit Hilfe eines Ersetzungsalgorithmuses. Hat sie ein<br />
gesetztes Dirty-Bit muß sie zuerst noch auf externen <strong>Speicher</strong> geschrieben werden. Die Seite,<br />
die eingeräumt werden soll, wird dann über eine externe Seitentafel gefunden. In ihr steht für<br />
jede Seite die Adresse, auf der sie sich auf dem externen <strong>Speicher</strong> befindet (vgl. Abb. 4.1).<br />
Nach dem Einräumen der Seite in den Hauptspeicher wird das dazugehörige Valid-Bit im<br />
entsprechenden Seitentafeleintrag gesetzt. Da der <strong>Speicher</strong>transfer sehr viel Zeit benötigt,<br />
muß die CPU in der Lage sein, während der Blockersetzung einen anderen Prozeß zu<br />
bearbeiten, falls einer vorhanden und von dem Seitenfehler nicht ebenfalls betroffen ist. Ist<br />
der <strong>Speicher</strong>transfer beendet, kann dann der vorherige Prozeß wiederhergestellt und weiter<br />
abgearbeitet werden. In der Praxis kommt ein Seitenfehler nur mit einer Wahrscheinlichkeit<br />
von 0.0001% vor, so daß man gut mit virtuellem <strong>Speicher</strong> arbeiten kann.
Abbildung 4.1: Seitenfehler<br />
Seitengröße<br />
Ein wichtiger Architekturparameter für virtuellen <strong>Speicher</strong> ist die Seitengröße. Einige Punkte<br />
sprechen für große, einige für kleine Seiten. Hier sind die Hauptpunkte:<br />
• Je größer die Seiten, desto kleiner die Seitentafel. Dadurch kann <strong>Speicher</strong> gespart<br />
werden.<br />
• Die Übertragung von großen Seiten ist effektiver, da der Aufwand für das Ein- und<br />
Ausräumen von Seiten sehr groß ist. Es ist besser einmal 4 KB zu übertragen als 4x1<br />
KB.<br />
• Für kleine Seiten spricht, daß diese nicht soviel <strong>Speicher</strong> verschwenden. Wenn ein<br />
Prozeß z.B. 5 KB benötigt, braucht er zwei Seiten (bei einer Seitengröße von 4 KB).<br />
Er hat also insgesamt 8 KB <strong>Speicher</strong>platz verbraucht, obwohl die letzte Seite noch 3<br />
KB frei hat. Diese können aber nicht anderweitig verwendet werden, was als interne<br />
Fragmentierung bezeichnet wird.<br />
• Zu große Seiten können unter Umständen E/A-Bandbreite verschwenden.<br />
Am häufigsten werden 4 KB große Seiten verwendet, da diese einen guten Kompromiß aus<br />
den verschiedenen Punkten bilden und die Adreßumsetzung dabei erfahrungsgemäß gut<br />
funktioniert.<br />
Seitengrößenerweiterung (page size<br />
extension)<br />
Die meisten Architekturen, die virtuellen <strong>Speicher</strong> implementieren, erlauben es, außer der<br />
Standard-Seitengröße, besonders große Seiten zu verwenden (z.B. die Pentiumarchitektur).<br />
Dies geschieht mit der Hilfe eines PSE-Bit (Page Size Extension-Bit) in der<br />
Verwaltungseinheit der Seitentafel. Ist es nicht gesetzt, werden die Standardseiten verwendet,<br />
falls es gesetzt ist, wird eine große Seite benutzt. Das findet vor allem Anwendung bei der<br />
Abbildung von Hardware Ein-/Ausgabebereichen in den <strong>Speicher</strong> (Memory Mapped I/O),
weil dabei oft ein sehr großer, kontinuierlicher <strong>Speicher</strong>bereich benötigt wird, z.B. bei<br />
Grafikkarten. Anwendung findet die Technik z.B. in der Pentiumarchitektur [3], die es erlaubt<br />
4 MB große Seiten, anstatt 4 KB große Seiten zu verwenden, falls das PSE-Bit gesetzt ist. Der<br />
Vorteil der Seitengrößenerweiterung ist, daß man weniger Verwaltungsaufwand hat. Es kann<br />
z.B. eine 4 MB große Seite, die nur einen Eintrag in der Seitetafel benötigt, benutzt werden,<br />
anstatt 1024 Einträge, die man bei einer Seitengröße von 4 KB benötigen würde.<br />
Adreßumsetzungspuffer (TLB)<br />
Die Adreßumsetzung mit Hilfe der Seitentafel ist mit zusätzlichen Hauptspeicherzugriffen<br />
verbunden. Man benötigt erst zwei Hauptspeicherzugriffe für die Adreßumsetzung (bei<br />
zweistufiger Seitentafel) und erst dann erfolgt der eigentliche Hauptspeicherzugriff, falls die<br />
Seite den Zustand valid hat. Um überhaupt sinnvoll mit virtuellem <strong>Speicher</strong> arbeiten zu<br />
können, muß daher die Adreßumsetzung beschleunigt werden. Man greift dabei wieder auf<br />
das Prinzip der Lokalität zu. Es ist wahrscheinlich, daß die Adresse, die gerade übersetzt<br />
wurde, nochmal benutzt wird oder auf eine benachbarte zugegriffen wird. Deshalb wird ein<br />
zusätzlicher Schnellspeicher verwendet, der die letzten Umsetzungen von Seitennummern in<br />
Rahmennummern enthält. Dieser Schnellspeicher wird in der Fachsprache TLB (translationlookaside<br />
buffer) genannt. Er besteht meist aus 64-Bit Einträgen, die<br />
Verwaltungsinfomationen, ein Tag und eine Rahmennummer beinhalten. Das Tag beinhaltet<br />
die Seitennummer zur Rahmennummer im zugehörigen Eintrag. Es wird benötigt, weil nicht<br />
jede Seitennummer im TLB Platz findet und somit nicht direkt über die Seitennummer<br />
indexiert werden kann, wie bei der Seitentafel.<br />
Abbildung 5.1: Funktionsweise eines TLB<br />
Bei jeder Adreßumsetzung wird nun die zu übersetzende Seitennummer mit den einzelnen<br />
Tags verglichen. Stimmen sie überein und ist das Valid-Bit des Eintrags gesetzt, so hat man<br />
einen TLB-Treffer und kann die Rahmennummer direkt aus dem Eintrag im TLB lesen, ohne<br />
einen Zugriff auf die Seitentafel. Nun muß nur noch der Offset von der virtuellen Adresse<br />
angehängt werden und dann kann der Hauptspeicherzugriff erfolgen. Ist die Seite aber nicht
im TLB oder das Valid-Bit des betreffenden Eintrags auf Null gesetzt, dann muß die<br />
Umsetzung der Adresse über die Seitentafel erfolgen. Die Umsetzung wird danach in den<br />
TLB eingeräumt, so daß beim nächsten Zugriff auf die Seite die Umsetzung schon im TLB<br />
ist, falls sie nicht schon wieder ersetzt worden ist. Der gesamte Vorgang ist in Abb. 5.1<br />
nochmals graphisch dargestellt. Für die Ersetzung im TLB werden meist eine Approximation<br />
des LRU- oder der Random-Algorithmus verwendet. Die Blockplazierung erfolgt<br />
vollassoziativ oder set-assoziativ. Set-assoziativ bedeutet, daß eine Seitennummer nur in<br />
einen bestimmten Bereich (set) des TLB abgebildet werden kann. Nach dem<br />
Konsistenzkriterium kann es einen TLB-Treffer nur geben, falls es auch einen Treffer in der<br />
Seitentafel geben würde (vgl. Tabelle 5.1). Ein TLB kann zwischen 32 und 4096 Einträge<br />
besitzen. Die Zugriffszeit bei einem Treffer beträgt 1 Taktzyklus, bei einem Fehler beträgt sie<br />
10 bis 30 Taktzyklen. Die Fehlerrate liegt zwischen 0.01% und 1%.<br />
<strong>Speicher</strong>schutz<br />
Weil zur selben Zeit mehrere Prozesse ablaufen und es verschiedene Benutzer gibt, muß ein<br />
Weg zum sicheren Schutz und Teilen von Code und Daten zwischen den einzelnen Prozessen<br />
gefunden werden. Dabei muß das Betriebssystem in der Lage sein, jederzeit von einem<br />
Prozeß in einen anderen umschalten zu können und die verschiedenen Prozesse gleichzeitig<br />
im Hauptspeicher zu halten. Es gibt verschiedene Methoden den <strong>Speicher</strong> zwischen den<br />
Prozessen aufzuteilen. Eine ist es, den Hauptspeicher in verschiedene Regionen für die<br />
Benutzerprozesse aufzuteilen. Die Regionen können verschiedene Größen haben. Dabei muß<br />
der Überwacher darauf achten, daß die Ressourcen unter Berücksichtigung einer<br />
ökonomischen Rechnerauslastung auf die Prozesse verteilt werden. Der Überwacher belegt<br />
einen festen, gegen Schreibzugriffe von Nutzerprozessen geschützten <strong>Speicher</strong>bereich, der in<br />
den realen <strong>Speicher</strong> abgebildet wird (vgl. Abb.6.1). Die Seiten der Regionen werden auf die<br />
restlichen Rahmen abgebildet bzw. auf den externen <strong>Speicher</strong>, falls nicht genügend Platz im<br />
Hauptspeicher ist.
Abbildung 6.1: Abbildung eines geschützten virtuellen <strong>Speicher</strong>s in den realen <strong>Speicher</strong>.<br />
Eine andere Möglichkeit ist es, jedem Prozeß den gesamten virtuellen Adreßraum zur<br />
Verfügung zu stellen. Ein Teil des virtuellen Adreßraumes von jedem Nutzerprozeß wird vom<br />
Überwacher belegt, es existiert aber nur eine Kopie davon im Hauptspeicher (vgl. Abb.6.2).<br />
Durch die Prozeß-ID sind die Adressen eindeutig bestimmt. Das ist vor allem für den TLB<br />
wichtig denn es wäre ineffektiv den TLB bei jeder Prozeßumschaltung ganz zu löschen, man<br />
könnte seine volle Kapazität gar nicht ausnutzen. Deswegen kann man einfach die Prozeß-ID<br />
im TLB-Eintrag verwenden, um damit die Einträge, die zu anderen Prozessen gehören, zu<br />
schützen, indem man die Prozeß-ID des aktiven Prozesses mit dem ID-Eintrag vergleicht.<br />
Abbildung 6.2: Seitenschutz, bei dem jeder Prozeß den vollen Adreßraum zur Verfügung<br />
stehen hat.<br />
Die Aufteilung des Hauptspeichers mit diesen beiden Methoden macht einen<br />
Schutzmechanismus nötig, um einen Prozeß zu hindern, einen anderen zu manipulieren oder<br />
seine Daten zu lesen. Andererseits sollen die Prozesse auch miteinander kommunizieren und<br />
auf Daten und Code gemeinsam zugreifen können. Am einfachsten läßt sich der Schutz mit<br />
Steuerbits in der Seitentafel realisieren. Man benutzt dafür z.B. ein Schreibschutz-Bit, um<br />
versehentliches Überschreiben von Code zu verhindern. Je nach Betriebssystem werden auch<br />
verschiedene Zugriffschutzbits für die verschiedenen Benutzerebenen verwendet und ein<br />
User-/Kernel-Bit, um zwischen Zugriffe von Benutzern oder vom Betriebssystem zu<br />
unterscheiden. Die Seitentafel darf nur im Kernel-Modus verändert werden, weil sonst der<br />
<strong>Speicher</strong>schutz nicht mehr funktionieren würde. Bei der gemeinsamen Nutzung von Code und<br />
Daten wird ein entsprechender Seitentafeleintrag in jedem Adreßraum eines Prozesses der auf
den Code und die Daten zugreifen können soll, eingerichtet, der auf den selben Rahmen im<br />
physikalischen <strong>Speicher</strong> zeigt. <strong>Speicher</strong>schutz erfordert auch einige Anforderungen an die<br />
Hardware, wie zwei verschiedene Modi, um zu entscheiden, ob der laufende Prozeß ein<br />
Betriebssystem- oder ein Nutzerprozeß ist und ein Mechanismus zum Umschalten vom<br />
Nutzermodus in den Supervisormodus und zurück.<br />
Zusammenfassung<br />
Die virtuelle <strong>Speicher</strong>technik wird vom Betriebssystem gesteuert und beruht auf dem Prinzip<br />
der Lokalität. Sie steuert automatisch die Ebenen Hauptspeicher und sekundärer <strong>Speicher</strong>, und<br />
läßt diese als eine Ebene erscheinen. Ihre Implementierung stellt Anforderungen sowohl an<br />
Hardware (z.B. MMU) als auch an Software (z.B. Seitenüberwacher). Die Implementierung<br />
ist zwar sehr komplex, aber es gibt bis heute noch keine Alternative für die Bewältigung des<br />
Problems der Fragmentierung des Hauptspeichers, des <strong>Speicher</strong>schutzes und dem des<br />
beschränkten Hauptspeichers. Der <strong>Speicher</strong>schutz wird durch die Verwendung von User-<br />
/Kernel-Bits, Schreibschutz-Bit und diversen Zugriffs-Bits in der Verwaltungseinheit der<br />
Seitentafel realisiert. Sie erlaubt effiziente und sichere Teilung des <strong>Speicher</strong>s für verschiedene<br />
Prozesse. Die Fragmentierung wird mit einer flexiblen Abbildung durch eine Seitentafel<br />
beseitigt. Einen größeren Adreßraum bekommt man dadurch, daß man die Ebene sekundärer<br />
<strong>Speicher</strong> mit einbezieht und automatisch verwaltet. Typische Werte für virtuellen <strong>Speicher</strong><br />
sind in Tabelle 7.1 zu sehen.<br />
Selbst wenn RAM immer günstiger und größer wird und man eigentlich genügend <strong>Speicher</strong><br />
auch für mehrere große Prozesse hat, ist die Implementierung des virtuellen <strong>Speicher</strong>s immer<br />
noch sinnvoll. Sie stellt ja nicht nur einen größeren <strong>Speicher</strong>bereich zur Verfügung, sondern<br />
bietet auch sonst noch einige Vorteile, wie <strong>Speicher</strong>schutz und flexiblere<br />
Hauptspeichernutzung. Deswegen ist eine Ablösung der virtuellen <strong>Speicher</strong>technik durch eine<br />
andere Technik auch noch nicht absehbar.<br />
Es gibt heute schon Betriebssysteme, die einen 64-Bit Adreßraum verwenden, wie das<br />
Betriebssytem Open VMS Alpha von DEC. Es benutzt 8 KB große Seiten und eine dreistufige<br />
Seitentafel, was einen virtuellen Adreßraum von 8 TB (1024 x 1024 x 1024 x 8KB) ergibt.<br />
Für die gesamte Abbildung in den <strong>Speicher</strong> werden dann, bei 8 Byte langen<br />
Seitentafelneinträgen, alleine insgesamt 8 GB (1024 x 1024 x 1024 x 8 Byte) für die<br />
Seitentafeln eines Prozesses benötigt. Das Betriebssystem ist vor allem für große
Datenbanksysteme geeignet. Einige Terabytes an virtuellen <strong>Speicher</strong> und mehrere Gigabytes<br />
an Hauptspeicher erlauben es, Datenbanken vollständig in den virtuellen <strong>Speicher</strong> zu laden,<br />
was viel Zeit bei Datenbankabfragen spart.<br />
Literatur<br />
1<br />
2<br />
3<br />
DAVID A. PATTERSON AND JOHN L. HENNESSY:<br />
Computer Organization & Design.<br />
2nd ed., Morgan-Kaufmann, 1997<br />
PAUL HERRMANN:<br />
Rechnerarchitektur.<br />
Vieweg, 1998<br />
DON ANDERSON AND TOM SHANLEY:<br />
Pentium Processor System Architecture.<br />
2nd ed., Addison-Wesley, 1995