Adressräume und Adressübersetzung

Lerne, wie ein Betriebssystem jedem laufenden Programm einen eigenen privaten, geschützten Speicher bereitstellt. Beginne mit dem Adressraum, den ein Programm sieht, und den Zielen der Speichervirtualisierung. Verfolge dann Heap-Anforderungen von malloc und free bis zu den Systemaufrufen, die dahinterstehen. Übersetze Adressen Schritt für Schritt mit Basis- und Grenzregistern sowie durch Segmentierung. Erfahre außerdem, wie Freispeicherverwaltungen Anforderungen unterschiedlicher Größe platzieren, vom First-Fit-Verfahren bis zum Buddy-Allokator, und dabei interne und externe Fragmentierung begrenzen.

MittelEinheit 3 von 9 in Kurs: Betriebssysteme – Prozesse, Speicher, Threads und Dateisysteme

Was du lernst

  1. Beschreibe den Adressraum, den ein laufendes Programm sieht, warum das Betriebssystem Speicher virtualisiert und welche Ziele jedes Verfahren zur Speichervirtualisierung erfüllen muss.
    • Beschreibe einen Adressraum und seinen Aufbau
      Erkläre, was ein Adressraum ist, wie Code, Heap und Stack darin angeordnet sind und worin sich virtuelle und physische Adressen unterscheiden.
    • Bewerte die Speichervirtualisierung anhand ihrer Ziele
      Bewerte ein Verfahren zur Speichervirtualisierung anhand von Transparenz, Effizienz und Schutz und unterscheide den Hardwaremechanismus von den Strategien des Betriebssystems.
  2. Unterscheide Stack- und Heap-Speicher, fordere Heap-Speicher mit den Standardaufrufen an und gib ihn wieder frei, erkenne mögliche Fehler und erkläre, woher der Speicher stammt.
    • Reserviere Speicher mit der Speicherverwaltungsschnittstelle und gib ihn wieder frei
      Unterscheide automatischen Stack-Speicher von Heap-Speicher, verwende malloc und free im Grundsatz korrekt und erkenne die wichtigsten Arten von Speicherfehlern.
    • Erkläre, wie der Allokator Speicher vom Betriebssystem erhält
      Unterscheide die Aufgabe des Betriebssystems, den Adressraum eines Prozesses zu verwalten, von der Aufgabe der Bibliothek, den Heap zu verwalten, und beschreibe die Systemaufrufe zum Vergrößern und Verkleinern des Speichers.
  3. Erkläre, wie hardwaregestützte dynamische Relokation jede virtuelle Adresse in eine physische umwandelt, was Hardware und Betriebssystem jeweils beitragen und wo die Grenzen des Verfahrens liegen.
    • Übersetze virtuelle Adressen mit Basis- und Grenzregistern
      Wende dynamische Relokation an, indem du zu jeder virtuellen Adresse die Basis addierst und sie anhand der Grenze prüfst, und vergleiche sie mit statischer Relokation.
    • Teile die Aufgaben zwischen Hardware und Betriebssystem auf
      Zähle auf, was die Hardware bereitstellen und was das Betriebssystem bei Prozesserzeugung, Kontextwechseln, Ausnahmen und Beendigung tun muss, damit das Verfahren mit Basis- und Grenzregister funktioniert.
    • Bewerte das Verfahren mit Basis- und Grenzregister
      Wäge Geschwindigkeit und Schutz des Verfahrens mit Basis- und Grenzregister gegen den verschwendeten Speicher ab und erkläre, warum es zur Segmentierung führt.
  4. Gib jedem logischen Segment eines Adressraums ein eigenes Basis- und Grenzregister, übersetze und prüfe Adressen mit Segmentregistern und beurteile, was die Segmentierung vom Betriebssystem verlangt.
    • Übersetze Adressen für jedes Segment getrennt
      Platziere Code, Heap und Stack unabhängig voneinander im physischen Speicher und übersetze eine Adresse, indem du ihren Offset innerhalb des Segments berechnest.
    • Bestimme, auf welches Segment eine Adresse verweist
      Bestimme das Segment anhand der obersten Bits einer virtuellen Adresse oder anhand der Art, wie die Adresse gebildet wurde, und ermittle den Offset binär.
    • Berücksichtige Stack, Speicherschutz und gemeinsame Speichernutzung
      Übersetze Stackadressen mit einem negativen Offset, verwende Schutzbits pro Segment und erkläre, wie Prozesse Code sicher gemeinsam nutzen.
    • Erkläre die Unterstützung der Segmentierung und bewerte das Verfahren
      Beschreibe, was das Betriebssystem für die Segmentierung tun muss, vergleiche grob- und feingranulare Segmentierung und erkläre die externe Fragmentierung, die das Verfahren begrenzt.
  5. Erkläre, wie eine Freispeicherverwaltung freien Speicher erfasst und Anforderungen unterschiedlicher Größe bedient, vergleiche Platzierungsstrategien und beurteile interne und externe Fragmentierung.
    • Unterscheide interne und externe Fragmentierung
      Definiere die beiden Arten der Fragmentierung, erkenne sie in Beispielen und erkläre, warum ein Heap-Allokator den Speicher nicht kompaktieren kann.
    • Erfasse freien Speicher mit einer Freispeicherliste
      Teile freie Blöcke auf und verschmelze sie, nutze Verwaltungsköpfe zur Freigabe ohne Größenangabe und lege die Freispeicherliste im freien Speicher selbst an.
    • Vergleiche Platzierungsstrategien
      Wende Best Fit, Worst Fit, First Fit und Next Fit auf eine Freispeicherliste an und wäge ihre Geschwindigkeit gegen die verursachte Fragmentierung ab.
    • Nutze nach Größenklassen getrennte Freispeicherlisten sowie Slab- und Buddy-Allokatoren
      Erkläre, wie größenspezifische Listen, Objektcaches und Buddy-Blöcke mit Zweierpotenzgrößen die Speicherzuweisung beschleunigen, und berechne Verschwendung und Verschmelzung beim Buddy-Allokator.

Adressräume und Adressübersetzung einfach erklärt

5 Kapitel

1

Eine private Speichersicht für jeden Prozess

Ein Zeiger gehört zu dem Prozess, der ihn verwendet. Sein Zahlenwert allein bezeichnet keine physische Speicherzelle. Adressräume ermöglichen es, mehrere Programme im Speicher zu halten, während jedes mit seiner eigenen Sicht auf Code und Daten läuft. Betriebssystem und Prozessor müssen diese Sicht erhalten, Zugriffe schnell übersetzen und verhindern, dass ein Programm ein anderes beschädigt. Diese Ziele erklären sowohl den Aufbau, den ein Programm sieht, als auch die dahinter verborgene Technik.

1.1 Der Speicher aus Sicht eines Prozesses

Ein laufendes Programm arbeitet mit einem Adressraum: seiner eigenen Sicht auf den Speicher mit nummerierten Bytepositionen. Das Betriebssystem platziert den Speicher des Programms im Rechner und sorgt dafür, dass seine Adressen die richtigen physischen Bytes erreichen. Der Aufbau aus Sicht des Programms und die Anordnung in der Hardware sind verschiedene Dinge.

Warum mehrere Programme im Speicher bleiben

Frühe Systeme konnten neben dem Betriebssystem ein Programm halten. Das Betriebssystem konnte beispielsweise die ersten 64 KB und das Programm den übrigen Speicher belegen. Der Mehrprogrammbetrieb hielt mehr als einen Prozess bereit, sodass die CPU arbeiten konnte, während ein anderer Prozess auf eine Ein- oder Ausgabe wartete. Das Zeitteilverfahren wechselte die CPU häufig genug zwischen Prozessen, um interaktive Benutzer zu bedienen.

Den gesamten Speicher eines Prozesses bei jedem Wechsel auf die Festplatte zu schreiben, würde den Wechsel viel zu langsam machen. Mehrere Prozesse im physischen Speicher zu halten, vermeidet dieses Kopieren, schafft aber ein Schutzproblem. Eine von einem Prozess erzeugte Adresse darf es ihm nicht erlauben, das Betriebssystem oder einen benachbarten Prozess zu überschreiben.

Speichergrößen folgen hier der in diesen Modellen verwendeten binären Konvention: 1 KB sind 1.024 Bytes, auch als 1 KiB geschrieben; 1 MB sind 1.024 KB. Ein 16 KB großer Adressraum hat 16.384 Bytepositionen, nummeriert von 0 bis 16.383. Die Zahl 16.384 ist die Endgrenze, keine gültige Byteadresse.

Code, Daten, Heap und Stack
BereichInhaltVerwendung
CodeMaschinenbefehleDie CPU ruft hier Befehle ab.
Statische DatenGlobale und statische lokale VariablenDer Speicher bleibt während der Programmausführung bestehen.
HeapZur Laufzeit angeforderte BlöckeDas Programm fordert Blöcke an und gibt sie frei.
StackZustand von Funktionsaufrufen und gewöhnliche lokale VariablenAufrufe erzeugen Stackframes; bei der Rückkehr werden sie freigegeben.

In einer klassischen Lehrbuchdarstellung mit 16 KB belegt der Code die niedrigen Adressen von 0 bis zur 1-KB-Grenze. Der Heap beginnt oberhalb des Codes und wächst nach oben. Der Stack beginnt an der oberen Grenze und wächst nach unten. Die freie Lücke dazwischen gibt beiden Bereichen Platz zum Wachsen. Reale Anordnungen unterscheiden sich; beispielsweise hat jeder Thread seinen eigenen Stack. Die Aufgaben der Bereiche bleiben auch dann hilfreich, wenn sich ihre Positionen ändern.

Verfolge einen Zeiger zum richtigen Speicher

Eine virtuelle Adresse bezeichnet ein Byte im Adressraum eines Prozesses. Eine physische Adresse bezeichnet ein Byte in der Speicherhardware des Rechners. Wenn C-Code printf("%p", (void *) &x) ausführt, gibt er eine virtuelle Adresse aus. Der ausgegebene Zeiger verrät nicht, welches physische Byte x speichert.

Angenommen, zwei Prozesse verwenden beide die virtuelle Adresse 1.000. Jede Adresse wird im eigenen Adressraum des jeweiligen Prozesses interpretiert, sodass die Zugriffe unterschiedliche physische Bytes erreichen können. Ebenso muss Code an der virtuellen Adresse 0 nicht die physische Adresse 0 belegen. Das Betriebssystem kann ihn anderswo platzieren und die Hardwareübersetzung so einrichten, dass sie ihn erreicht.

Der Prozessor übersetzt Befehlsabrufe sowie Lese- und Schreibzugriffe. Das Betriebssystem wählt und verwaltet die Abbildungen; dabei kann es auch physische Speicherorte ändern und die virtuellen Adressen des Programms stabil halten.

Wichtige Erkenntnisse

  • Ein Adressraum ist die Sicht eines Prozesses auf den Speicher, nicht seine physische Platzierung.
  • Code, statische Daten, Heap und Stack erfüllen unterschiedliche Speicheraufgaben.
  • Virtuelle Adressen werden innerhalb eines Prozesses interpretiert; gleiche Werte können unterschiedliche physische Bytes erreichen.
  • Ein 16 KB großer Adressraum enthält nach der binären KB-Konvention die Bytes 0 bis 16.383.

1.2 Transparenz, Effizienz und Schutz

Ein Speichersystem muss mehr leisten, als eine Zahl korrekt zu übersetzen. Es muss gewöhnliche Programme ausführen lassen, ohne dass diese ihre physische Platzierung verwalten, die Übersetzung mit geringem Aufwand durchführen und die Grenzen durchsetzen, die gemeinsame Nutzung sicher machen. Diese Anforderungen heißen Transparenz, Effizienz und Schutz.

Bewahre die Sicht des Programms

Transparenz bedeutet, dass ein Programm so läuft, als hätte es privaten Speicher. Es verwendet seine eigenen Adressen für Code und Daten, ohne zu verfolgen, welche anderen Prozesse vorhanden sind oder wo deren Bytes liegen. Betriebssystem und Hardware bewahren diese Sicht im Hintergrund. Um einen Prozess im physischen Speicher zu verschieben, sollte der Prozess seine Zeiger nicht umschreiben müssen.

Die Illusion erfordert keine eigene physische Kopie jedes Bytes für jeden Prozess. Geschützter Code, der nur gelesen werden darf, kann beispielsweise gemeinsam genutzt werden. Entscheidend ist, dass die gemeinsame Nutzung das Verhalten und die Zugriffsrechte bewahrt, auf die jeder Prozess Anspruch hat.

Berücksichtige beide Arten von Aufwand

Jeder Befehlsabruf sowie jeder Lese- und Schreibzugriff muss übersetzt werden. Eine Betriebssystemroutine für jeden Zugriff auszuführen, würde die gewöhnliche Ausführung zu langsam machen. Die Hardware führt die häufigen Operationen aus; Betriebssystemcode behandelt Einrichtung, Änderungen und Fehler.

Auch Speichereffizienz ist wichtig. Ein kleiner, schneller Übersetzungsmechanismus kann dennoch erheblichen Speicher verschwenden. Ein einzelner Bereich mit Basis- und Grenzregister reserviert die ungenutzte Lücke zwischen Heap und Stack. Bei der Segmentierung muss für diese Lücke kein physischer Speicher bereitgestellt werden, dafür entstehen aber physische Bereiche unterschiedlicher Größe und Lücken dazwischen. Metadaten, ungenutzter reservierter Speicher und Lücken beeinflussen alle, wie viel nützliche Arbeit in den Speicher passt.

ZielFrage
TransparenzVerwendet das Programm weiterhin seine eigenen Adressen, ohne die Platzierung zu verwalten?
ZeiteffizienzWie viel zusätzlichen Aufwand verursacht jeder Speicherzugriff?
SpeichereffizienzWie viel Speicher wird durch Metadaten oder unbrauchbaren Platz verbraucht?
SchutzKann der Prozess nur auf Bereiche zugreifen und Operationen ausführen, die ihm erlaubt sind?
Halte einen unzulässigen Zugriff an der Grenze auf

Ohne Schutz könnte ein fehlgeleiteter Zeiger in einem Texteditor einen anderen Prozess oder den Kernel überschreiben. Der Schaden könnte sich viel später in einem anderen Codeteil zeigen. Werden Grenzen durchgesetzt, weist die Hardware einen Zugriff außerhalb des erlaubten Bereichs ab und löst eine Ausnahme aus. Das Betriebssystem behandelt die Ausnahme und beendet üblicherweise den fehlerhaften Prozess, wobei häufig eine Speicherzugriffsverletzung gemeldet wird.

Schutz isoliert Prozesse voneinander. Er macht die Speichernutzung in C innerhalb eines erlaubten Heap-Bereichs nicht sicher. Ein Schreibzugriff über einen zugewiesenen Block hinaus kann weiterhin einen anderen Block desselben Prozesses treffen. Prozessgrenzen und Grenzen von Blöcken des Allokators sind unterschiedliche Grenzen.

Trenne die Operation von der Entscheidung

Ein Mechanismus beschreibt, wie eine Operation ausgeführt wird. Das Addieren eines Basisregisterwerts und das Prüfen eines Grenzregisters sind Mechanismen. Eine Strategie wählt zwischen möglichen Handlungen: wo ein Prozess platziert wird, welche freie Lücke genutzt wird oder wann ein Segment verschoben wird.

Diese Trennung erlaubt es dem Betriebssystem, seine Platzierungsstrategie zu ändern und dieselbe Hardwareübersetzung beizubehalten. Speichervirtualisierung beginnt mit dieser privaten, geschützten Sicht; sie ist nicht durch die Nutzung der Festplatte als zusätzlicher Speicher definiert.

Wichtige Erkenntnisse

  • Transparenz bewahrt die Sicht des Programms, wenn sich die physische Platzierung ändert.
  • Zeiteffizienz ist bei jedem Speicherzugriff wichtig; zur Speichereffizienz gehören ungenutzte Bereiche und Metadaten.
  • Schutz isoliert Prozesse, setzt aber nicht jede Grenze eines Heap-Blocks durch.
  • Mechanismen führen Operationen aus; Strategien wählen zwischen Alternativen.
  • Die Hardware übersetzt gewöhnliche Zugriffe; das Betriebssystem verwaltet die Übersetzung und behandelt Fehler.
2

Von C-Speicherzuweisungen zu Betriebssystembereichen

Eine Heap-Anforderung durchläuft zwei Ebenen der Speicherverwaltung. Die C-Bibliothek findet einen Block für das Programm, während das Betriebssystem die Bereiche bereitstellt und schützt, aus denen Blöcke herausgeteilt werden. Anhand dieser Ebenen lässt sich erklären, warum eine Zeigervariable und der ihr zugewiesene Speicher unterschiedliche Lebensdauern haben, warum die Freigabe eines Blocks die Speichernutzung des Prozesses unverändert lassen kann und warum eine erfolgreiche Kompilierung wenig darüber aussagt, ob spätere Heap-Zugriffe zulässig sind.

2.1 Blöcke anfordern und ihre Lebensdauer beachten

Eine Zeigervariable und der Speicher, auf den sie zeigt, sind getrennte Objekte. Ihre Lebensdauern zu verstehen, ist der Ausgangspunkt für die C-Speicherverwaltungsschnittstelle. Gewöhnliche lokale Variablen nutzen typischerweise automatischen Stack-Speicher, Heap-Blöcke bleiben bis zu ihrer Freigabe bestehen, und globale sowie lokale static-Variablen haben während der gesamten Programmausführung Speicher.

Verfolge die Speicherzuweisung, nicht nur den Zeiger
void f(void) {
    int x;
    int *p = malloc(sizeof(int));
}

Im üblichen Stackaufbau gehören x und p zu diesem Aufruf von f. Die angeforderte Ganzzahl gehört zum Heap. Bei der Rückkehr wird die lokale Zeigervariable entfernt; der Heap-Block wird dadurch nicht freigegeben. Bleibt keine Kopie der Adresse erhalten, hat das Programm den Zugriff auf eine aktive Speicherbelegung verloren: ein Speicherleck.

malloc(size) fordert mindestens so viele Bytes an und gibt einen Zeiger zurück oder NULL, wenn die Anforderung nicht erfüllt werden kann. Die Bytes werden für das Programm nicht initialisiert. Verwende für 10 Ganzzahlen malloc(10 * sizeof(int)); ist eine Ganzzahl 4 Bytes groß, fordert das 40 Bytes an. Reserviere für eine Zeichenkette ihre Zeichen plus ein Byte für das abschließende Nullzeichen: malloc(strlen(s) + 1).

sizeof(a) misst das Objekt mit dem Namen a. Ist a ein Zeiger, ergibt sich die Größe des Zeigers, nicht die Größe des zugewiesenen Speichers. Ein 8 Byte großer Zeiger kann auf einen 40 Byte großen Block verweisen.

Kenne die Aufgaben der Bibliotheksoperationen
AufrufHauptzweck
mallocEinen Block erhalten, dessen Bytes nicht gelöscht sind.
callocEinen mit Nullbytes gefüllten Block erhalten.
reallocDie Größe eines Blocks ändern und ihn gegebenenfalls verschieben.
freeEinen zugewiesenen Block an den Allokator zurückgeben.

free erhält den zurückgegebenen Zeiger ohne Größenargument. Der Allokator speichert Metadaten zur Größe, üblicherweise in einem Verwaltungskopf vor dem Block. Übergib den zurückgegebenen Zeiger eines aktuell zugewiesenen Blocks; ein Zeiger ins Innere des Blocks ist kein gültiger Ersatz. free(NULL) ist zulässig und bewirkt nichts. Diese Funktionen sind Routinen der C-Bibliothek, keine Systemaufrufe.

Erkenne Fehler an Lebensdauer oder Grenze

Ein Zeiger ohne gültige Speicherzuweisung darf nicht so verwendet werden, als zeige er bereits auf Speicher. Neu zugewiesene Bytes vor ihrer Initialisierung zu lesen, ist ein Lesen nicht initialisierter Daten. Schreiben über das Ende des zugewiesenen Bereichs hinaus ist ein Pufferüberlauf; das Weglassen des abschließenden Bytes einer Zeichenkette kann einen solchen verursachen.

Nach free sind alte Zeiger auf den Block hängende Zeiger. Ein Zugriff über einen solchen Zeiger ist ein Zugriff nach Freigabe. Den Block erneut freizugeben, ist eine doppelte Speicherfreigabe. Die Freigabe einer Adresse, die der Allokator nicht für einen aktiven Block zurückgegeben hat, ist eine ungültige Freigabe. Ein Speicherleck ist etwas anderes: Der Block bleibt zugewiesen, aber das Programm gibt ihn nicht mehr frei, obwohl es das sollte.

Verstehe, was die Hardware prüfen kann

Die Kompilierung kann trotz all dieser Fehler gelingen. Die Hardware prüft normalerweise Prozessbereiche und Berechtigungen, nicht jede Speicherzuweisung der Bibliothek. Das Überschreiten einer Blockgrenze innerhalb des zulässigen Heaps des Prozesses kann undefiniertes Verhalten auslösen, ohne sofort einen Fehler zu verursachen. Das Programm kann Daten beschädigen, später abstürzen, bedeutungslose Werte lesen oder scheinbar funktionieren. Valgrind und AddressSanitizer können viele solcher Fehler während der Ausführung erkennen.

Wichtige Erkenntnisse

  • Die Lebensdauer eines Heap-Blocks ist von der Lebensdauer der Zeigervariablen getrennt.
  • Verwende sizeof für den gespeicherten Typ; sizeof auf einem Zeiger misst nicht dessen Block.
  • malloc, calloc, realloc und free sind Bibliotheksroutinen.
  • Die Speicherzuweisung für eine Zeichenkette benötigt Platz für ihr abschließendes Nullbyte.
  • free(NULL) bewirkt nichts; ein Zeiger ins Innere eines zugewiesenen Blocks ist kein gültiger Zeiger zur Freigabe des Blocks.
  • Die Bereichsprüfungen der Hardware können Speicherfehler an Blockgrenzen innerhalb des Heaps übersehen.

2.2 Wie der Allokator seinen Speicher erhält

Die C-Bibliothek und das Betriebssystem verwalten Speicher auf unterschiedlichen Ebenen. Das Betriebssystem richtet Bereiche im Adressraum eines Prozesses ein und stellt den zugehörigen physischen Speicher bereit. Der Allokator teilt verfügbaren Heap-Speicher in Blöcke auf, erfasst freie Stücke und verwendet sie über mehrere Aufrufe hinweg erneut.

Verschiebe den Program Break

Der Program Break ist die Adresse unmittelbar hinter dem Ende des traditionellen Heaps. brk setzt diese Grenze auf eine gewählte Adresse. sbrk(n) verschiebt sie um eine Anzahl von Bytes und gibt bei Erfolg den alten Program Break zurück. sbrk(0) meldet den aktuellen Program Break, ohne ihn zu verschieben.

Angenommen, der Program Break liegt bei 40.960 und der Allokator ruft erfolgreich sbrk(8192) auf. Der Rückgabewert ist 40.960. Die neue Grenze ist:

40.960+8.192=49.152

Die hinzugefügten Bytes belegen die virtuellen Adressen 40.960 bis 49.151. Der neue Program Break, 49.152, liegt unmittelbar hinter dem hinzugefügten Bereich. Verwechselt man den Rückgabewert mit der neuen Grenze, verschiebt das jede spätere Platzierungsberechnung.

Anwendungen nutzen die Speicherverwaltungsbibliothek, statt den Program Break direkt zu verschieben. Die Bibliothek erfasst, welche Teile ihres Bereichs ihr gehören. Unabhängige Änderungen dieser Grenze können ihre Verwaltungsdaten ungültig machen.

Erhalte eine separate Speicherabbildung

mmap kann einen anonymen Bereich erzeugen: Speicher, der nicht an eine Datei gebunden und anfangs mit Nullbytes gefüllt ist. munmap entfernt eine Speicherabbildung. Ein Allokator kann separate Speicherabbildungen für große Speicherzuweisungen verwenden und sie bei der Freigabe an das Betriebssystem zurückgeben.

In glibc liegt der anfängliche Standardschwellenwert für diesen Weg bei 128 KB oder 131.072 Bytes und kann nach oben angepasst werden. Eine ausreichend große Speicherzuweisung erhält typischerweise eine separate Speicherabbildung, wenn sie nicht aus vorhandenem freiem Speicher bedient werden kann. Dies ist eine Strategie der Bibliothek, keine allgemeingültige Trennlinie in der Sprache C.

Gib auf Bibliotheks- oder Betriebssystemebene frei
EreignisWas sich ändert
Einen kleinen wiederverwendbaren Block freigebenDer Allokator kann daraus eine weitere Anforderung bedienen.
Eine separate Speicherabbildung entfernenDer abgebildete Bereich gehört nicht mehr zum Prozess.
Den Prozess beendenDas Betriebssystem gewinnt den Speicher des Prozesses zurück.

Ein Aufruf von free gibt einen kleinen Block üblicherweise an den Speichervorrat der Bibliothek zurück. Der Prozess kann danach daher denselben Speicherverbrauch ausweisen. Der Speicher steht zur Wiederverwendung bereit, ohne das Betriebssystem erneut anfordern zu müssen.

Beim Prozessende werden auch Speicherzuweisungen zurückgewonnen, die das Programm verloren hat. Während der Lebensdauer eines langlebigen Servers verbrauchen wiederholte Speicherlecks jedoch immer mehr seines verfügbaren Speichers. Die Rückgewinnung beim Beenden verhindert diesen Speicherdruck während der Ausführung nicht.

Verbinde Heap-Wachstum und Übersetzung

Im Segmentierungsmodell ist der Heap ein zusammenhängendes physisches Segment. Damit er wachsen kann, müssen unmittelbar darüber freie physische Bytes liegen. Sind diese Bytes belegt, kann das Betriebssystem das Segment in eine größere Lücke verschieben und seinen Übersetzungszustand aktualisieren. Ist kein geeigneter Bereich verfügbar, schlägt das Wachstum fehl. Kann der Allokator nicht genügend Platz erhalten, gibt malloc NULL zurück.

Wichtige Erkenntnisse

  • Das Betriebssystem stellt Bereiche bereit; die Bibliothek unterteilt sie in Blöcke.
  • Ein erfolgreiches sbrk gibt den alten Program Break zurück, nicht den neuen.
  • Ein anonymes mmap beschafft einen separaten, mit Nullen gefüllten Bereich; munmap entfernt ihn.
  • Die Freigabe eines Blocks erlaubt meist seine Wiederverwendung, ohne den vom Betriebssystem zugewiesenen Speicher des Prozesses zu verringern.
  • Ein segmentierter Heap benötigt zusammenhängenden physischen Speicher, um zu wachsen.
3

Übersetzung mit Basis- und Grenzregister

Das Verfahren mit Basis- und Grenzregister wandelt eine prozessrelative Adresse mit einem Vergleich und einer Addition in eine physische Adresse um. Seine Einfachheit macht die Aufgabenteilung gut sichtbar: Die Hardware prüft jeden Zugriff, während das Betriebssystem einen Platz auswählt, beim Prozesswechsel den Übersetzungszustand wiederherstellt und Fehler behandelt. Verfolgst du einen Prozess durch diese Ereignisse, zeigt sich auch der größte Nachteil: Selbst ungenutzte Adressen zwischen Heap und Stack belegen einen Teil seines physischen Platzes.

3.1 Eine Adresse prüfen und übersetzen

Dynamische Relokation übersetzt Adressen während der Programmausführung. Im Modell mit Basis- und Grenzregister belegt ein Prozess einen zusammenhängenden physischen Bereich. Beginne mit gleich großen Adressräumen, die in den physischen Speicher passen; ein Registerpaar beschreibt dann den Bereich des aktuell laufenden Prozesses.

Prüfe zuerst die virtuelle Adresse

Das Basisregister enthält die physische Adresse des ersten Bytes des Bereichs. Das Grenzregister, auch Limitregister genannt, enthält die Größe des Bereichs in Bytes. Für eine nicht negative virtuelle Adresse gilt:

0≤virtuelle Adresse<Grenze

Ist die Prüfung erfolgreich, gilt:

physische Adresse=Basis+virtuelle Adresse

Andernfalls löst die Hardware eine Ausnahme aus. Eine Größe ist eine Anzahl, daher liegt die Adresse, die der Grenze entspricht, bereits außerhalb des Bereichs. Negative virtuelle Adressen sind ebenfalls ungültig.

Gehe die Bytegrenzen durch

Betrachte einen 8 KB großen Adressraum mit Basis 20.480 und Grenze 8.192. Seine virtuellen Bytes reichen von 0 bis 8.191. Die virtuelle Adresse 1.000 besteht die Prüfung und erreicht das physische Byte 21.480. Das letzte zulässige virtuelle Byte, 8.191, erreicht 28.671. Adresse 9.000 besteht die Prüfung nicht, weil sie mindestens 8.192 beträgt; die Hardware muss sie abweisen, bevor sie den physischen Zugriff ausführt.

Befehlsabrufe verwenden dieselbe Übersetzung wie Daten. Lädt ein Befehl an der virtuellen Adresse 128 von der virtuellen Adresse 3.000, erreicht der Abruf die physische Adresse 20.608 und der Lesezugriff 23.480. Das sind zwei Speicherzugriffe mit zwei übersetzten Adressen, keine gemeinsame Übersetzung für den gesamten Befehl.

Halte die Grenzkonventionen auseinander
Inhalt des GrenzregistersWas verglichen wird
BereichsgrößeDie virtuelle Adresse muss kleiner als die Größe sein.
Physische EndgrenzeDie übersetzte Adresse muss unter dieser Grenze liegen.

Derselbe 8-KB-Bereich hat die physische Endgrenze 28.672. Beide Konventionen erlauben genau die Bytes 20.480 bis 28.671. Der Vergleich der physischen Adresse 21.480 mit der Größe 8.192 vermischt die beiden Konventionen und weist einen zulässigen Zugriff fälschlich ab.

Verschiebe Speicher, ohne Zeiger zu ändern

Die MMU, die Speicherverwaltungseinheit, führt die Prüfungen und die Addition aus. Das Betriebssystem kann einen Prozess anhalten, seinen Bereich an eine andere Stelle kopieren und die Basis ändern. Der Prozess verwendet weiterhin dieselben virtuellen Adressen, weshalb die Relokation dynamisch ist.

Bei der statischen Relokation schreibt dagegen ein Ladeprogramm eingebettete Adressen beim Laden des Codes um. Wird ein Verweis von 1.000 auf 21.480 umgeschrieben, wird eine physische Platzierung im Programm festgehalten. Ein späteres Verschieben erfordert weiteres Umschreiben, und das Umschreiben allein hindert ein Programm nicht daran, eine Adresse außerhalb seines Bereichs zu bilden.

Addiere eine Basis nur zu einer virtuellen Adresse. Sie zu einer bereits übersetzten physischen Adresse zu addieren, verschiebt den Speicherort ein zweites Mal und zerstört die Abbildung.

Wichtige Erkenntnisse

  • Enthält das Grenzregister eine Größe, prüfe die virtuelle Adresse, bevor du die Basis addierst.
  • Der zulässige Bereich schließt die Adresse aus, die der Grenze entspricht.
  • Sowohl Befehlsabrufe als auch Datenzugriffe werden übersetzt.
  • Eine physische Endgrenze und eine Grenze als Größe in Bytes erfordern unterschiedliche Vergleiche.
  • Dynamische Relokation ändert die Platzierung, ohne virtuelle Zeiger umzuschreiben.

3.2 Übersetzungszustand einrichten und wechseln

Die Hardware enthält Übersetzungsregister für den Prozess, der auf einer CPU läuft. Das Betriebssystem verwahrt den gespeicherten Zustand wartender Prozesse. Der Schutz hängt davon ab, dass nur privilegierter Betriebssystemcode diesen Zustand ändern darf.

Stelle die Hardwareoperationen bereit

Die CPU benötigt Benutzer- und Kernelmodus, ein Basis- und Grenzregisterpaar in ihrer MMU sowie Schaltungen für Prüfung und Addition. Außerdem benötigt sie privilegierte Operationen, um das Registerpaar zu ändern und Ausnahmebehandlungsroutinen zu installieren. Unzulässige Adressen und Versuche, privilegierte Befehle im Benutzermodus auszuführen, müssen Ausnahmen auslösen.

Könnte ein Benutzerprogramm seine Basis ändern, könnte es seine Adressen in den Kernel oder in einen anderen Prozess umlenken. Die Grenzprüfungen würden dann den falschen Bereich schützen. Die Beschränkung von Änderungen ist daher Teil des Schutzmechanismus und nicht nur eine administrative Erleichterung.

Erzeuge und starte einen Prozess

Beim Systemstart installiert das Betriebssystem seine Trap- und Ausnahmebehandlungsroutinen, startet den Interrupt-Timer und bereitet seine Aufzeichnungen über freien Speicher vor. Wenn ein Prozess startet, findet das Betriebssystem einen physischen Platz für seinen Adressraum. Bei gleich großen Plätzen kann eine Freispeicherliste einfach erfassen, welche Plätze ungenutzt sind.

Das Betriebssystem markiert den gewählten Platz als belegt, erfasst Basis und Grenze des Prozesses, lädt die Register und wechselt in den Benutzermodus. Der Prozess läuft dann direkt auf der CPU. Die Hardware übersetzt gewöhnliche Zugriffe, ohne für jedes Byte das Betriebssystem aufzurufen.

Stelle beim Kontextwechsel das richtige Paar wieder her

Ein Timerinterrupt übergibt die Kontrolle an das Betriebssystem. Es sichert die Register des bisherigen Prozesses einschließlich Basis und Grenze in dessen Prozesskontrollblock und stellt dann die Werte des nächsten Prozesses wieder her, bevor es in den Benutzermodus zurückkehrt.

Angenommen, die Basis von A ist 20.480 und die von B 40.960, beide mit der Grenze 8.192. Die virtuelle Adresse 1.000 von A erreicht 21.480. Nach dem Wechsel zu B erreicht derselbe virtuelle Wert 41.960. Würden die CPU-Register ohne den Übersetzungszustand wiederhergestellt, würde B mit seinen Zeigern auf den Bereich von A zugreifen.

Die CPU enthält das aktive Paar; jeder Prozess hat sein eigenes gespeichertes Paar. Stelle dir weder ein einziges dauerhaftes Paar für den ganzen Rechner vor noch nimm an, dass die Paare aller Prozesse gleichzeitig auf einer CPU aktiv sind.

Verschieben, Fehler behandeln und beenden

Um A an die physische Adresse 49.152 zu verschieben, nimmt das Betriebssystem A von der CPU, kopiert seinen 8-KB-Bereich und aktualisiert die gespeicherte Basis von A. Sobald A mit dieser geladenen Basis weiterläuft, erreicht die virtuelle Adresse 1.000 die Adresse 50.152. Seine virtuellen Zeigerwerte müssen nicht geändert werden.

Bei einem ungültigen Zugriff löst die Hardware eine Ausnahme aus und übergibt die Ausführung im Kernelmodus an die registrierte Behandlungsroutine. Das Betriebssystem beendet üblicherweise den verursachenden Prozess. Bei der Beendigung gibt es den Platz an die Freispeicherliste zurück und gibt die Prozessdaten frei.

Wichtige Erkenntnisse

  • Nur privilegierter Code kann Übersetzungsregister ändern.
  • Die CPU enthält den aktiven Zustand; Prozesskontrollblöcke enthalten den gespeicherten Zustand.
  • Ein Kontextwechsel muss Basis und Grenze zusammen mit den anderen Registern wiederherstellen.
  • Für die Relokation muss der Prozess angehalten, sein Speicher kopiert und seine gespeicherte Basis aktualisiert werden.
  • Die Hardware löst bei Zugriffsfehlern Ausnahmen aus; das Betriebssystem behandelt sie.
  • Bei der Beendigung wird der physische Platz an die Freispeicherliste zurückgegeben.

3.3 Warum ein einfacher Platz Speicher verschwendet

Das Verfahren mit Basis- und Grenzregister erfüllt zwei Ziele mit geringem Aufwand: Die Übersetzung ist transparent und jede Adresse wird anhand des zugewiesenen Bereichs geprüft. Seine größte Schwäche liegt im Verhältnis zwischen diesem Bereich und den Bytes, die der Prozess tatsächlich nutzt.

Reserviere die gesamte Spanne

Die Hardware benötigt bei der häufigen Ausführung nur eine Addition und einen Vergleich. Ein Allokator für Plätze fester Größe kann zudem schnell einen freien Platz auswählen. Keiner dieser Vorteile bedeutet, dass der Platz mit nützlichen Daten gefüllt ist.

In der klassischen Anordnung liegen Code und Heap nahe dem einen Ende und der Stack nahe dem anderen. Ein Basis- und Grenzregisterpaar übersetzt das gesamte Intervall. Die ungenutzte Lücke zwischen Heap und Stack belegt ebenso physischen Speicher wie die genutzten Teile.

Für einen 16 KB großen Adressraum, der 1 KB Code, 2 KB Heap und 1 KB Stack nutzt:

genutzt=1+2+1=4 KB ungenutzt=16−4=12 KB ungenutzter Anteil=1216=75%

Die Verschwendung ist interne Fragmentierung: ungenutzte Bytes innerhalb eines bereits einem Prozess zugewiesenen Bereichs. Sie entsteht nicht dadurch, dass der Heap-Allokator einen schlechten Block wählt. Selbst ein lückenlos gefüllter 2-KB-Heap lässt die große Lücke in der Mitte reserviert.

Unterscheide drei Größenfragen

Die Spanne des Adressraums, die aktuell genutzten Bytes und der verfügbare physische Speicher sind unterschiedliche Größen. Ein byteadressierter 32-Bit-Adressraum umfasst 232 Bytes oder 4 GB. Ihn als einen physischen Block zu reservieren, erfordert die gesamten 4 GB, selbst wenn der Prozess aktuell nur einige Megabyte nutzt.

EinschränkungFolge
Ein zusammenhängender BereichGetrennte freie Lücken können einen Prozessbereich nicht aufnehmen.
Gleich große PlätzeKleine Prozesse verschwenden Platz; größere passen möglicherweise nicht hinein.
Ein dünn besetzter AdressraumUngenutzte virtuelle Lücken verbrauchen weiterhin reservierten physischen Speicher.

Unterschiedlich große Plätze heben die Beschränkung auf gleiche Größen auf, beseitigen aber nicht den Bedarf an einer zusammenhängenden Lücke. Außerdem entsteht ein Platzierungsproblem: Das Betriebssystem muss zwischen freien Lücken unterschiedlicher Größe wählen. Wird ein Platz verkleinert, können die niedrigen Codeadressen und eine weit entfernte hohe Stackadresse nicht beide erhalten bleiben, wenn die gesamte Spanne in derselben einfachen Abbildung gültig bleiben muss.

Relokiere die Bereiche getrennt

Code, Heap und Stack müssen im physischen Speicher nicht benachbart sein, damit die Sicht des Programms erhalten bleibt. Erhält jeder Bereich eine eigene Basis und Grenze, kann das Betriebssystem nur für seine genutzte Ausdehnung Speicher zuweisen und die virtuelle Lücke ohne physischen Speicher lassen. Das ist Segmentierung, die Verallgemeinerung eines Relokationspaars auf mehrere logische Bereiche.

Für die obige 16-KB-Anordnung benötigen die drei genutzten Bereiche vor ihrem eigenen Verwaltungsaufwand insgesamt nur 4 KB. Die Speicherersparnis entsteht dadurch, dass andere Einheiten relokiert werden, nicht dadurch, dass die Addition des Basisregisterwerts schneller wird.

Wichtige Erkenntnisse

  • Die gesamte virtuelle Spanne belegt einen zusammenhängenden physischen Platz.
  • Ungenutzter Speicher innerhalb dieses Platzes ist interne Fragmentierung.
  • Gleich große Plätze beschränken große Prozesse und verschwenden Platz bei kleinen.
  • Segmentierung spart Speicher, indem sie genutzte Bereiche unabhängig voneinander relokiert.
4

Übersetzung mit getrennten Segmenten

Ein einzelnes Übersetzungsregisterpaar reserviert die Lücken innerhalb eines Adressraums. Segmentierung gibt Code, Heap und Stack unabhängige physische Speicherorte, sodass nur für ihre aktuelle Ausdehnung Speicher bereitgestellt werden muss. Zur Übersetzung einer Adresse gehört nun, das Segment und seinen Offset zu finden; beim nach unten wachsenden Stack kommt eine Vorzeichenänderung hinzu. Die gewonnene Flexibilität ermöglicht Schutz und gemeinsame Codenutzung, verlangt aber vom Betriebssystem, zusammenhängende Lücken für Bereiche unterschiedlicher Größe zu finden.

4.1 Jedem Bereich eine eigene Übersetzung geben

Ein Segment ist ein zusammenhängender Bereich eines Adressraums mit einem bestimmten Zweck. Im klassischen Modell erhalten Code, Heap und Stack jeweils ein eigenes Basis- und Größenregisterpaar. Ihre physischen Bereiche können unabhängig voneinander platziert werden, während der Prozess seinen vertrauten virtuellen Aufbau behält.

Stelle nur für die genutzten Bereiche Speicher bereit

Verwende einen 16 KB großen Adressraum mit den folgenden Platzierungen. Die Größen verwenden binäre KB oder KiB.

SegmentVirtueller BereichPhysische Basis oder obere GrenzeGröße
Code0 bis 2.047Basis 40.9602.048 Bytes
Heap4.096 bis 7.167Basis 26.6243.072 Bytes
Stack14.336 bis 16.383Obere Grenze 53.2482.048 Bytes

Code und Heap wachsen zu höheren Adressen hin; die Stackübersetzung verwendet die obere Grenze und einen negativen Offset. Die drei physischen Bereiche benötigen zusammen nur 7 KB. Die virtuellen Lücken dazwischen benötigen keinen physischen Speicher. Ein Segment bleibt zusammenhängend; nur die Segmente selbst müssen nicht mehr nebeneinanderliegen.

Übersetze ein nach oben wachsendes Segment

Bestimme zuerst das Segment. Berechne dann den Offset von seinem virtuellen Anfang aus. Prüfe, ob der Offset nicht negativ und kleiner als die aktuelle Segmentgröße ist, und addiere ihn dann zur physischen Basis:

Offset=virtuelle Adresse−virtueller Segmentanfang physische Adresse=Segmentbasis+Offset

Für die virtuelle Adresse 500 ist der Code-Offset 500. Er liegt unter 2.048, sodass der Zugriff die physische Adresse 41.460 erreicht.

Für die virtuelle Adresse 5.000 ist der Heap-Offset:

5.000−4.096=904

Die Größenprüfung ist erfolgreich, weil 904 unter 3.072 liegt. Das Ergebnis ist:

26.624+904=27.528

Die vollständige virtuelle Adresse zur Heap-Basis zu addieren, ergäbe 31.624. Dabei würde fälschlich die virtuelle Anfangsposition des Heaps in eine Berechnung übernommen, die nur den Abstand innerhalb des Segments benötigt.

Prüfe das Segment, nicht nur die Spanne des Adressraums

Die virtuelle Adresse 7.500 passt zwar noch in 14 Bits, ist aber deshalb nicht zulässig. Ihr Heap-Offset beträgt 3.404 und liegt damit außerhalb des aktuellen Bereichs von 3.072 Bytes. Die Hardware löst eine Ausnahme aus. Eine Adresse kann also innerhalb der gesamten Spanne des Adressraums und dennoch außerhalb jedes gültigen Bereichs liegen.

Der Begriff Speicherzugriffsverletzung entstand im Zusammenhang mit ungültigen Zugriffen bei der Segmentierung und wird weiterhin für unzulässige Zugriffe auf Systemen mit anderen Übersetzungsverfahren verwendet. Der Fehlername allein bezeichnet nicht den Übersetzungsmechanismus des Rechners.

Wichtige Erkenntnisse

  • Jedes Segment hat eine eigene Basis und Größe.
  • Der Offset eines nach oben wachsenden Segments wird von dessen virtuellem Anfang aus gemessen.
  • Addiere nur den geprüften Offset zur physischen Basis.
  • Eine Adresse innerhalb der gesamten virtuellen Spanne kann dennoch eine Ausnahme auslösen.
  • Ungenutzte virtuelle Lücken zwischen Segmenten benötigen keinen physischen Speicher.

4.2 Das Segment auswählen und seinen Offset bestimmen

Bevor die Hardware Basis und Größe eines Segments verwenden kann, muss sie entscheiden, welches Segment ein Zugriff bezeichnet. Ein expliziter Entwurf legt diese Auswahl in Adressbits ab. Ein impliziter Entwurf leitet sie daraus ab, wie die CPU die Adresse gebildet hat.

Teile eine 14-Bit-Adresse auf

Verwende die obersten zwei Bits für die Segmentnummer und die unteren zwölf Bits für den Offset. Das Zwei-Bit-Feld hat vier mögliche Werte. Verwende 00 für Code, 01 für den Heap und 11 für den Stack; lasse 10 ungenutzt.

Das Offsetfeld kann 4.096 verschiedene Werte von 0 bis 4.095 darstellen. Das maximale virtuelle Fenster jedes Segments ist daher 4 KB groß, selbst wenn der aktuell gültige Bereich des Segments kleiner ist. Maximale Kapazität und aktuelle Größe sind unterschiedliche Begrenzungen.

Für die virtuelle Adresse 5.000 lautet die 14-Bit-Darstellung:

01 0011 1000 1000

Das führende 01 wählt den Heap. Das verbleibende 0011 1000 1000 entspricht dezimal 904. Bei einer Heap-Größe von 3.072 und einer physischen Basis von 26.624 besteht der Offset die Größenprüfung und erreicht die physische Adresse 27.528.

Verwende eine Maske und eine Verschiebung

Dieselbe Aufteilung lässt sich als Code ausdrücken:

segment = (va & 0x3000) >> 12;
offset = va & 0x0FFF;

Die Maske 0x3000 behält die beiden Selektorbits; die Verschiebung um zwölf Stellen bewegt sie an die unteren Positionen und ergibt so die Segmentnummer. Die Maske 0x0FFF setzt diese Selektorbits auf null und behält den Offset. Für va = 0x1388 sind die Ergebnisse Segment 1 und Offset 0x388 oder 904.

Addiere 0x1388 nicht direkt zur Heap-Basis. Der Wert enthält sowohl den Segmentselektor als auch den Offset. Der Selektor wählt die Register; nur der Offset geht in die Übersetzung innerhalb des Segments ein. Die resultierende physische Adresse muss das Bitmuster des Selektors nicht beibehalten.

Erkenne die Kosten der Aufteilung

Ein Zwei-Bit-Selektor erzeugt vier Fenster. Bleibt 10 ungenutzt, ist ein Viertel der virtuellen Spanne von 16 KB nicht verfügbar. Kein Segment kann über sein 4-KB-Fenster hinaus wachsen, nur weil mehr physischer Speicher frei wird. Andere Entwürfe können eine andere Aufteilung verwenden; fasst man Code und Heap in einem Segment zusammen, kann ein einzelnes Selektorbit dieses vom Stack unterscheiden.

Lasse stattdessen die Zugriffsart entscheiden

Bei einem impliziten Entwurf verwenden Befehlsabrufe das Codesegment, weil ihre Adresse aus dem Befehlszähler stammt. Verweise über den Stapelzeiger oder Framepointer verwenden das Stacksegment. Andere Zugriffe verwenden in diesem vereinfachten Modell das Heap-Segment.

AuswahlmethodeWoher die Auswahl stammt
ExplizitEin Segmentfeld innerhalb der virtuellen Adresse
ImplizitDas Register oder die Zugriffsart, mit dem oder der die Adresse gebildet wurde

Wichtige Erkenntnisse

  • Selektorbits wählen den Übersetzungszustand; Offsetbits bestimmen ein Byte innerhalb des Fensters.
  • Zwölf Offsetbits erlauben ein maximales Segmentfenster von 4.096 Bytes.
  • Maskierung und Verschiebung trennen die Felder, ohne Selektorbits in die Addition zur Basis zu übernehmen.
  • Die implizite Auswahl nutzt die Quelle einer Adresse, etwa den Befehlszähler oder den Stapelzeiger.

4.3 Den Stack übersetzen und Berechtigungen durchsetzen

Der Stack wächst zu niedrigeren Adressen hin. Seine Übersetzung benötigt neben Basis und Größe ein Richtungsbit. In diesem Modell steht 1 für Wachstum nach oben und 0 für Wachstum nach unten. Zugriffsberechtigungen bilden eine weitere unabhängige Prüfung: Ein Byte kann innerhalb der Grenzen liegen und die angeforderte Operation trotzdem verbieten.

Betrachte die Stackbasis als obere Grenze

Bei einem nach oben wachsenden Segment bezeichnet die Basis sein erstes physisches Byte. Bei einem nach unten wachsenden Segment bezeichnet sie die Grenze unmittelbar oberhalb seines höchstgelegenen physischen Bytes. Im durchgehenden Beispiel umfassen die Stackbasis 53.248 und die Größe 2.048 die physischen Bytes 51.200 bis 53.247.

Wandle für das durch 11 ausgewählte 4-KB-Stackfenster das zwölf Bit große Offsetfeld in einen negativen Versatz um:

Versatz=Offsetfeld−4.096

Das Feld reicht von 0 bis 4.095, daher ist dieser Versatz immer negativ. Er ist gültig, wenn sein Betrag die aktuelle Stackgröße nicht überschreitet. Addiere dann den vorzeichenbehafteten Versatz zur Basis.

Rechne einen zulässigen Stackzugriff durch

Die virtuelle Adresse 15.500 wählt den Stack. Sein Fenster beginnt virtuell bei 12.288, daher ist ihr Offsetfeld 3.212. Wandle das Feld um:

3.212−4.096=−884

Die Tiefe 884 liegt innerhalb der Größe 2.048. Die Übersetzung ergibt:

53.248−884=52.364

Die positive Zahl 3.212 zur Basis zu addieren, würde auf eine Stelle oberhalb des Stacks verweisen. Das Feld ist noch nicht der Versatz, der für ein nach unten wachsendes Segment benötigt wird.

Die virtuelle Adresse 13.000 ergibt dagegen das Feld 712 und den Versatz −3.384. Seine Tiefe überschreitet 2.048, sodass die Hardware eine Ausnahme auslöst. Vergrößert das Betriebssystem den Stack erfolgreich, können durch die größere Stackgröße weitere niedrige Adressen gültig werden.

Berücksichtige den unteren Endpunkt

Ein Versatz von −2.048 erreicht das niedrigste Byte des Stacks, 51.200, und ist zulässig. Ein Versatz von −2.049 liegt außerhalb. Die Prüfung nach unten schließt daher einen Betrag ein, der der Größe entspricht. Das ergibt dieselbe Byteanzahl wie die Regel für Wachstum nach oben, obwohl diese einen Offset ausschließt, der der Größe entspricht: Die Basis eines nach oben wachsenden Segments ist ein Byte, die eines nach unten wachsenden Segments eine Endgrenze.

Prüfe die angeforderte Operation

Schutzbits erfassen Lese-, Schreib- und Ausführungsberechtigung. Code darf typischerweise gelesen und ausgeführt werden; Heap und Stack dürfen typischerweise gelesen und beschrieben werden. Ein Schreibzugriff auf Code mit Lese- und Ausführungsberechtigung löst selbst bei gültigem Offset eine Ausnahme aus. Auch das Ausführen von Daten löst eine Ausnahme aus, wenn die Ausführungsberechtigung fehlt.

Code, der nur gelesen werden darf, kann gemeinsam genutzt werden. Das Betriebssystem kann die Codesegmente zweier Prozesse auf denselben physischen Bereich verweisen lassen und beiden Schreibzugriffe verbieten. Die Befehlsbytes belegen eine physische Kopie, während jeder Prozess einen eigenen Heap und Stack behält.

Wichtige Erkenntnisse

  • Die Basis eines nach unten wachsenden Segments bezeichnet seine obere Grenze, kein gespeichertes Byte.
  • Ziehe die maximale Fenstergröße vom Offsetfeld ab, um den negativen Versatz zu erhalten.
  • Das niedrigste Byte des Stacks ist zulässig, wenn der Betrag des Versatzes der aktuellen Größe entspricht.
  • Sowohl die Grenzen als auch die angeforderte Operation müssen die Hardwareprüfungen bestehen.
  • Die gemeinsame Nutzung von Code ohne Schreibberechtigung spart physischen Speicher, während private beschreibbare Bereiche getrennt bleiben.

4.4 Physische Segmente unterschiedlicher Größe verwalten

Segmentierung ersetzt einen großen Bereich durch mehrere kleinere. Dadurch muss für leere virtuelle Lücken kein physischer Speicher bereitgestellt werden. Das Betriebssystem muss nun aber Bereiche unterschiedlicher Größe platzieren und vergrößern und dabei den Übersetzungszustand jedes Prozesses bewahren.

Sichere jeden Teil der Abbildung

Bei einem Kontextwechsel reichen Basis und Größe allein nicht mehr aus. Das Betriebssystem sichert Basis, Größe, Wachstumsrichtung und Berechtigungen jedes Segments und stellt sie wieder her. Würde ein Stack mit der falschen Richtung oder Code mit den falschen Berechtigungen wiederhergestellt, änderte das die Bedeutung ansonsten identischer Adressen.

Benötigt ein Allokator mehr Heap-Speicher, muss das Betriebssystem das physische Heap-Segment erweitern. Freie Bytes unmittelbar darüber erlauben ein direktes Wachstum. Andernfalls kann das Betriebssystem es in eine geeignete größere Lücke verschieben und seine Basis aktualisieren. Kann es keinen geeigneten Bereich erhalten, schlägt das Wachstum fehl.

Wähle die Granularität
VerfahrenOrganisationFolge für die Verwaltung
Grobgranulare SegmentierungEinige Bereiche wie Code, Heap, StackEin kleiner Satz Segmentregister beschreibt den Prozess.
Feingranulare SegmentierungViele Bereiche, möglicherweise einer pro DatenstrukturEine im Speicher liegende Segmenttabelle ermöglicht mehr Möglichkeiten zur gemeinsamen Nutzung und zum Schutz.

Multics verwendete feingranulare Segmentierung. Mehr Segmente erlauben kleinere Kontrolleinheiten, erfordern aber mehr Übersetzungsmetadaten und Verwaltung.

Finde eine ausreichend große Lücke

Physische Segmente unterschiedlicher Größe hinterlassen freie Lücken unterschiedlicher Größe, wenn Prozesse starten, wachsen und enden. Externe Fragmentierung entsteht, wenn insgesamt genügend Bytes frei sind, aber keine einzelne Lücke das angeforderte Segment aufnehmen kann.

Angenommen, die freien Lücken sind 8 KB, 6 KB und 10 KB groß. Zusammen sind das 24 KB. Ein 12 KB großes Heap-Segment passt dennoch nicht hinein: Die größte verfügbare Lücke ist nur 10 KB groß. Die Lücken auf dem Papier zusammenzufassen, macht ihre physischen Adressen nicht benachbart.

Kompaktierung kann diese Anordnung bereinigen. Das Betriebssystem hält Prozesse an, kopiert Segmente nebeneinander und aktualisiert ihre Basen. Virtuelle Adressen funktionieren weiterhin, weil die Übersetzung die physischen Verschiebungen verbirgt. Das Ergebnis ist ein großer zusammenhängender freier Bereich, doch das Kopieren ist aufwendig und neue Speicherzuweisungen können den Speicher erneut fragmentieren.

Platzierungsstrategien wie First Fit und Best Fit versuchen, ungünstige Reste ohne Kopieren zu begrenzen. Keine Platzierungsstrategie für unterschiedliche Größen beseitigt externe Fragmentierung für jede Folge von Anforderungen. Auch ein einzelnes großes Segment kann innerhalb seiner eigenen Ausdehnung Platz verschwenden, wenn nur verstreute Teile genutzt werden.

Erkenne das Modell in modernen Systemen

Seitenverwaltung teilt Speicher in Stücke fester Größe und vermeidet, dass jeder logische Bereich eine einzige physische Lücke belegen muss. Das Segmentierungsmodell bleibt hilfreich, um Bereiche, Schutz und gemeinsame Nutzung zu verstehen. Im 64-Bit-Modus von x86-64 gelten die gewöhnlichen Segmentbasen für Code, Daten und Stack als null und ihre Grenzen werden nicht geprüft; FS und GS behalten nutzbare Basen für Daten pro Thread oder CPU. Eine Meldung über einen unzulässigen Zugriff als Speicherzugriffsverletzung bedeutet nicht, dass das klassische Modell mit drei Segmenten aktiv ist.

Wichtige Erkenntnisse

  • Kontextwechsel stellen neben Basen und Größen auch Richtung und Berechtigungen wieder her.
  • Feingranulare Segmentierung stellt über eine Segmenttabelle mehr unabhängige Bereiche bereit.
  • Externe Fragmentierung hängt von den Größen einzelner Lücken ab, nicht nur von ihrer Summe.
  • Kompaktierung verschiebt physische Segmente und aktualisiert Basen, während virtuelle Zeiger erhalten bleiben.
  • Platzierungsstrategien verringern Fragmentierung, ohne ihre Beseitigung zu garantieren.
  • Die gewöhnliche x86-64-Übersetzung unterscheidet sich von der klassischen Segmentierung.
5

Freie Blöcke und ihre Kosten verwalten

Ein Allokator muss wissen, welche Bytes frei sind, und für jede Anforderung einen ausreichend großen Block auswählen. Blockaufteilung und Verschmelzen verändern die Freispeicherliste; Platzierungsstrategien bestimmen, welche Lücken für spätere Anforderungen übrig bleiben. Verwaltungsköpfe, Speicherausrichtung und das Aufrunden auf Zweierpotenzen verursachen eigene Kosten. Wenn du diese Änderungen verfolgst, kannst du fehlgeschlagene Speicherzuweisungen erklären, Strategien vergleichen und verstehen, warum spezialisierte Caches und Buddy-Allokatoren Flexibilität gegen Geschwindigkeit eintauschen.

5.1 Verschwendung innerhalb oder zwischen Blöcken erkennen

Die Freispeicherverwaltung erfasst verfügbaren Speicher und wählt aus, wo jede Anforderung platziert wird. Gleich große Anforderungen können eine Liste austauschbarer Blöcke nutzen. Unterschiedliche Größen sind schwieriger: Sowohl ein Heap-Allokator als auch ein Betriebssystem, das Segmente platziert, muss einen zusammenhängenden Block finden, der für die nächste Anforderung groß genug ist.

Erkenne externe Fragmentierung

Betrachte einen 60 Byte großen Bereich mit freien Bytes 0–19, einem zugewiesenen Stück bei 20–39 und freien Bytes 40–59. Vierzig Bytes sind frei, aber keine der beiden freien Lücken kann eine Anforderung von 25 Bytes aufnehmen. Das aktive Stück in der Mitte verhindert, dass die Lücken als ein Bereich behandelt werden.

Das ist externe Fragmentierung: Außerhalb zugewiesener Stücke ist Kapazität frei, aber ihre Anordnung macht sie für eine bestimmte zusammenhängende Anforderung unbrauchbar. Die freien Bytes sind nicht verschwunden. Sie können weiterhin kleinere Anforderungen bedienen oder nutzbar werden, wenn dazwischenliegende Stücke freigegeben werden.

Erkenne interne Fragmentierung

Ein Allokator, der Anforderungen auf Vielfache von 16 aufrundet, gibt für eine Anforderung von 20 Bytes ein 32 Byte großes Stück aus. Die 12 zusätzlichen Bytes liegen innerhalb eines bereits vergebenen Stücks. Diese Verschwendung ist interne Fragmentierung.

Die ungenutzte Lücke zwischen Heap und Stack in einem Platz mit Basis- und Grenzregister ist ein weiteres Beispiel. Der Buddy-Allokator verursacht dieselbe Art von Verschwendung, wenn er auf Zweierpotenzen aufrundet. Die Verwaltungsebene ist unterschiedlich, aber der Ort des ungenutzten Speichers ist das bestimmende Merkmal.

VerschwendungWo sie liegtWarum sie ins Gewicht fällt
Interne FragmentierungInnerhalb eines zugewiesenen BereichsDie reservierten Bytes übersteigen den nutzbaren Inhalt.
Externe FragmentierungIn getrennten freien LückenKeine einzelne Lücke ist groß genug für den angeforderten Bereich.

Ein Allokator kann unter beidem leiden. Das Aufrunden einer Anforderung kann Bytes innerhalb ihres Stücks verschwenden, während andernorts freie Blöcke getrennt bleiben.

Verschmelze freie Nachbarn, ohne aktive Daten zu verschieben

Das Verschmelzen freier Blöcke verbindet physisch benachbarte freie Blöcke. Wird das mittlere Stück im 60-Byte-Bereich freigegeben, können alle drei Stücke zu einer 60 Byte großen Lücke verschmelzen. Bleibt das mittlere Stück zugewiesen, kann das Verschmelzen es nicht überbrücken. Verschmelzen verringert externe Fragmentierung, garantiert aber nicht für jede Anforderung eine ausreichend große Lücke.

Mehr Speicher für einen Heap kann den Druck verringern, ändert aber nicht die Adressen bestehender aktiver Blöcke. Ihre Platzierung kann weiterhin unbrauchbare kleine Lücken hinterlassen.

Erkläre, warum das Betriebssystem Segmente kompaktieren kann

Das Betriebssystem kann physische Segmente kopieren und ihre Basen aktualisieren. Programme behalten virtuelle Adressen, sodass die Abbildung diese Verschiebungen verbergen kann. Ein allgemeiner C-Heap-Allokator hat keine Übersetzungsschicht zwischen zurückgegebenen Zeigern und Stellen innerhalb des Heaps. Ein Programm kann einen Zeiger in viele Objekte kopiert haben, und der Allokator kann nicht jede Kopie finden und umschreiben.

Würde ein aktiver Heap-Block allein verschoben, würden diese Zeiger weiterhin auf seine alte Position verweisen. Deshalb hält die gewöhnliche malloc-Verwaltung aktive Blöcke an ihren zurückgegebenen Adressen, bis sie freigegeben werden. Sie muss die Fragmentierung durch Platzierung, Wiederverwendung, Aufteilung und Verschmelzen freien Speichers handhaben.

Wichtige Erkenntnisse

  • Interne Verschwendung liegt innerhalb zugewiesener Bereiche; externe Fragmentierung liegt in getrennten freien Lücken.
  • Die gesamte freie Kapazität belegt nicht, dass eine zusammenhängende Anforderung hineinpasst.
  • Das Verschmelzen verbindet benachbarte freie Blöcke, ohne aktive Blöcke zu verschieben.
  • Die Relokation von Segmenten durch das Betriebssystem kann Zeiger durch Übersetzung erhalten; eine gewöhnliche Kompaktierung des C-Heaps kann das nicht.

5.2 Verwaltungsköpfe, Aufteilungen und Verschmelzungen verfolgen

Eine Freispeicherliste erfasst freie Blöcke nach Adresse und Größe. Bei einer Zuweisung wird ein Block aufgeteilt oder aus der Liste entfernt; bei einer Freigabe wird ein Stück zurückgegeben und möglicherweise mit Nachbarn verschmolzen. Die Größenkonvention ist wichtig: Eine erfasste Größe kann die nutzbaren Nutzdaten oder das gesamte Stück einschließlich Metadaten bezeichnen.

Teile einfache Bereiche auf und verschmelze sie

Ignoriere zunächst Verwaltungsköpfe. In einem 60 Byte großen Bereich mit den freien Einträgen (0, 20) und (40, 20) gibt eine am Anfang platzierte Anforderung von 8 Bytes die Adresse 0 zurück. Die verbleibenden Einträge sind (8, 12) und (40, 20).

Beginne erneut mit der ursprünglichen Anordnung und gib die zugewiesenen Bytes 20–39 in der Mitte frei. Ohne Verschmelzen können drei Einträge zu je 20 Bytes eine Anforderung von 30 Bytes einzeln weiterhin nicht bedienen. Das Verschmelzen benachbarter Bereiche ergibt (0, 60), womit dies möglich ist. Die Adressreihenfolge hilft dem Allokator, Nachbarn zu erkennen; benachbarte Listeneinträge allein reichen nicht aus, wenn ihre physischen Bereiche nicht aneinandergrenzen.

Finde den Verwaltungskopf über den Benutzerzeiger

free(ptr) erhält keine Größe. Ein einfacher Allokator speichert einen Verwaltungskopf mit der Größe unmittelbar vor den Nutzdaten, manchmal zusammen mit einem Prüfwert oder einer magischen Zahl. Durch Abziehen der Länge des Verwaltungskopfs findet er die Metadaten.

Bei einem 16 Byte großen Verwaltungskopf belegt eine Anforderung von 96 Bytes insgesamt 112 Bytes. Das Programm erhält einen Zeiger 16 Bytes nach dem Anfang des Stücks. Die Freigabe gibt den gesamten Bereich von 112 Bytes einschließlich Verwaltungskopf für die weitere Verwaltung zurück. Die Speicherausrichtung kann eine Aufrundung hinzufügen; diese Beispielgrößen erfüllen bereits die Ausrichtung auf 16 Bytes.

Verfolge eine eingebettete Freispeicherliste

Angenommen, ein 4.096 Byte großer Heap beginnt bei 32.768. Ein freier Knoten benötigt 16 Bytes und erfasst die nutzbaren Bytes nach dem Knoten. Anfangs erfasst er 4.080 nutzbare Bytes.

Drei Anforderungen von je 96 Bytes verbrauchen jeweils 112 Bytes:

AnforderungAdresse des VerwaltungskopfsZurückgegebener Zeiger
Erste32.76832.784
Zweite32.88032.896
Dritte32.99233.008

Der verbleibende freie Knoten beginnt bei 33.104. Seine nutzbare Größe beträgt 3.744 Bytes. Die Rechnung lautet:

336+16+3.744=4.096

Die Freigabe des Zeigers 32.896 führt zum Verwaltungskopf bei 32.880. Dieses Stück wird zu einem freien Knoten mit 96 nutzbaren Bytes. Es kann nicht mit dem letzten Knoten bei 33.104 verschmelzen, weil die dritte Speicherzuweisung dazwischenliegt. Werden alle drei Speicherzuweisungen freigegeben und verschmolzen, beschreibt ein Knoten wieder 4.080 nutzbare Bytes. Durch das Einbeziehen der alten inneren Verwaltungsköpfe werden deren Bytes innerhalb des verschmolzenen Bereichs verfügbar.

Halte die Verwaltungsdaten im verwalteten Bereich

Eingebettete Listenknoten liegen im freien Speicher selbst, sodass die Verwaltung der Liste keine separate Speicherzuweisung für jeden Eintrag benötigt. Der Knoten enthält eine Größe und einen Verweis auf den nächsten freien Block. Passt kein Block, kann der Allokator mit sbrk oder mmap weiteren Speicher anfordern; kann er nicht genug Speicher erhalten, führt das zu NULL.

Ein Überlauf kann einen benachbarten Verwaltungskopf oder Knoten überschreiben. Das erklärt, warum der sichtbare Fehler bei einem späteren malloc oder free auftreten kann, nachdem der Schreibzugriff die Liste beschädigt hat.

Wichtige Erkenntnisse

  • Unterscheide die nutzbare Größe vom gesamten Bereich einschließlich Metadaten.
  • Ein 16 Byte großer Verwaltungskopf lässt eine Anforderung von 96 Bytes 112 Bytes verbrauchen.
  • Finde einen Verwaltungskopf, indem du seine Größe vom zurückgegebenen Zeiger abziehst.
  • Blockaufteilung erhält einen nutzbaren Rest; Verschmelzen erfordert benachbarte freie Bereiche.
  • Eingebettete Knoten speichern Listenmetadaten in den freien Blöcken selbst.
  • Ein dazwischenliegendes aktives Stück verhindert das Verschmelzen.
  • Das Überschreiben von Verwaltungsdaten kann bei späteren Allokatoraufrufen Fehler verursachen.

5.3 Einen freien Block auswählen

Eine Platzierungsstrategie wählt zwischen Blöcken, die für eine Anforderung groß genug sind. Unterschiedliche Entscheidungen hinterlassen unterschiedliche Lücken für spätere Anforderungen. Neben der Fragmentierung spielt die Suchzeit eine Rolle, und keine Strategie ist für jede Arbeitslast überlegen.

Vergleiche die Suchregeln
StrategieAuswahlWichtigster Kompromiss
Best FitKleinster passender BlockEine vollständige Suche in einer einfachen Liste kann winzige Reste hinterlassen.
Worst FitGrößter BlockEine vollständige Suche in einer einfachen Liste kann eine wertvolle große Lücke verbrauchen.
First FitErster passender Block in ListenreihenfolgeEndet früh, aber kleine Reste sammeln sich nahe dem Anfang.
Next FitErster passender Block ab einer gespeicherten SuchpositionVerteilt Suchen über die Liste und beginnt bei Bedarf wieder am Anfang.

Für Best Fit und Worst Fit muss eine einfache unsortierte Liste vollständig durchsucht werden. Andere Strukturen können passende Größen schneller finden. First Fit bedeutet den ersten Block in der gewählten Listenreihenfolge; das muss nicht die kleinste Adresse sein, sofern die Liste nicht nach Adressen sortiert ist.

Verfolge die erste Anforderung

Nimm freie Blöcke von 30, 12, 50 und 20 Bytes in Adressreihenfolge. Ignoriere Verwaltungsköpfe und nimm an, dass bei der Aufteilung jeder Rest erhalten bleiben kann. Eine Anforderung von 18 Bytes verändert die Liste wie folgt:

  • Best Fit nutzt 20 und hinterlässt 30, 12, 50, 2.
  • Worst Fit nutzt 50 und hinterlässt 30, 12, 32, 20.
  • First Fit nutzt 30 und hinterlässt 12, 12, 50, 20.
  • Next Fit beginnt beim vierten Block, nutzt 20 und hinterlässt 30, 12, 50, 2.

Next Fit benötigt eine Anfangsposition, um die Auswahl zu bestimmen. Der Name allein bezeichnet nicht den ersten Eintrag der Liste. Bei der Verfolgung muss dieser Suchzustand zwischen Anforderungen erhalten bleiben.

Sieh, warum die nächste Anforderung das Urteil ändert

Eine anschließende Anforderung verlangt 45 Bytes. Best Fit und First Fit haben beide den 50-Byte-Block erhalten, daher gelingt die Anforderung bei beiden. Der größte verbleibende Block bei Worst Fit ist 32 Bytes groß, sodass die Anforderung trotz insgesamt 94 freier Bytes fehlschlägt:

30+12+32+20=94

Dieser Fehlschlag ist externe Fragmentierung. Die frühere Entscheidung hat die einzige ausreichend große Lücke aufgeteilt. Best Fit hat dagegen einen 2 Byte großen Rest hinterlassen, der für nützliche spätere Anforderungen zu klein sein kann. Ein unmittelbar kleiner Rest ist kein Beleg dafür, dass eine Strategie den Speicher über die gesamte Folge von Anforderungen gut nutzt.

Stimme Strategie und Datenstruktur aufeinander ab

Das Durchsuchen Tausender Einträge kann aufwendig sein, selbst wenn jede Platzierung gut ist. Praktische Allokatoren gruppieren Blöcke nach Größe oder verwenden durchsuchbare Strukturen wie balancierte Bäume. Eine nach Adressen sortierte Liste unterstützt außerdem das Verschmelzen, weil physische Nachbarn leichter zu finden sind.

Der Zuweisungsmechanismus (finden, aufteilen, Metadaten aktualisieren) kann gleich bleiben, während sich die Strategie ändert. Um Strategien fair zu vergleichen, verfolge dieselben Anforderungen und Freigaben, halte die Regeln für den Verwaltungsaufwand gleich und berücksichtige sowohl die entstehenden Lücken als auch den Suchaufwand.

Wichtige Erkenntnisse

  • Best Fit wählt den kleinsten ausreichenden Block; Worst Fit wählt den größten.
  • First Fit hängt von der Listenreihenfolge ab; Next Fit hängt zusätzlich von der gespeicherten Suchposition ab.
  • Eine Wahl, die eine Anforderung bedient, kann eine spätere größere Anforderung verhindern.
  • Keine Platzierungsstrategie ist für jede Arbeitslast die beste.
  • Datenstrukturen beeinflussen den Suchaufwand unabhängig von der Auswahlregel.

5.4 Größen, Objekte und Buddy-Blöcke wiederverwenden

Die allgemeine Suche in einer Freispeicherliste ist flexibel, kann aber aufwendig sein. Spezialisierte Allokatoren machen häufige Anforderungen vorhersehbar, indem sie Größen gruppieren, wiederverwendbare Objekte erhalten oder Blockgrößen auf Zweierpotenzen beschränken.

Verwende häufige Größen und initialisierte Objekte wieder

Nach Größenklassen getrennte Freispeicherlisten halten für häufige Größen separate Listen bereit und verwenden für andere Anforderungen einen allgemeinen Allokator. Eine Anforderung einer häufigen Größe kann direkt mit einem Block aus ihrer Liste bedient werden. Gleich große Blöcke erzeugen untereinander keine externe Fragmentierung, aber die Aufteilung der Kapazität auf Listen ist eine Strategieentscheidung: Übrige Blöcke einer Größe können eine andere Größe möglicherweise nicht bedienen.

Ein Slab-Allokator unterhält einen Objektcache für einen wiederkehrend benötigten Kernelobjekttyp, etwa einen Inode, eine Sperre oder einen Prozessdeskriptor. Ein Slab ist in Objekte dieses Typs unterteilt. Der Cache erhält bei Bedarf weitere Slabs und kann einen vollständig freien Slab an einen allgemeinen Allokator zurückgeben. Jeff Bonwick entwickelte diesen Ansatz für Solaris.

Der Cache hält freigegebene Objekte in einem initialisierten Zustand und vermeidet so wiederholtes Einrichten und Zerstören. Das spart über die Suche selbst hinaus Zeit.

Teile einen Buddy-Bereich auf

Ein binärer Buddy-Allokator beginnt mit einem Bereich, dessen Größe eine Zweierpotenz ist, und rundet jede Anforderung auf eine Blockgröße auf, die einer Zweierpotenz entspricht. Er teilt einen freien Block wiederholt in gleiche Hälften, bis eine Hälfte gerade noch passt. Die beiden Hälften einer Teilung sind Buddies.

Runde für eine Anforderung von 5 KB in einem 64-KB-Bereich 5 KB auf 8 KB auf. Teile 64 in zwei 32-KB-Blöcke, dann den gewählten 32-KB-Block in zwei 16-KB-Blöcke und anschließend den gewählten 16-KB-Block in zwei 8-KB-Blöcke. Die Rückgabe des ersten 8-KB-Blocks hinterlässt freie Buddies bei den Offsets 8 KB, 16 KB und 32 KB mit den jeweiligen Größen 8 KB, 16 KB und 32 KB.

Die Zuweisung verschwendet 3 KB innerhalb ihres 8-KB-Blocks. Das ist interne Fragmentierung, die für regelmäßiges Aufteilen und Verschmelzen in Kauf genommen wird.

Finde den einzig zulässigen Partner zum Verschmelzen

Für Offsets, die vom Anfang des verwalteten Bereichs aus gemessen werden, gilt:

Buddy-Offset=Blockoffset⊕Blockgro¨ße

Das exklusive Oder kehrt das der Blockgröße entsprechende Bit um. Bei einem 8-KB-Block mit Offset 0 liegt sein Buddy bei Offset 8.192. Bei einem 16-KB-Block mit Offset 16.384 liegt sein Buddy bei 0.

Wird der erste 8-KB-Block freigegeben, verschmilzt er mit seinem freien 8-KB-Buddy. Der entstehende 16-KB-Block kann mit seinem freien Buddy verschmelzen, danach kann der entstehende 32-KB-Block erneut verschmelzen. Das Verschmelzen endet, wenn der benötigte gleich große Buddy belegt ist. Dass zwei Blöcke benachbart und frei sind, reicht nicht aus, wenn sie keine Buddies aus derselben Teilung sind.

Eine Anforderung von 20 KB erhält 32 KB, wobei 12 KB oder 37,5 % des Blocks verschwendet werden. Linux kombiniert einen Buddy-Allokator für physischen Speicher mit Objektcaches nach dem Slab-Prinzip. Allokatoren für Programme mit mehreren Threads verwenden außerdem lokale Caches oder mehrere Arenen, um das Warten auf eine gemeinsame Zuweisungssperre zu verringern; glibc, jemalloc und tcmalloc nutzen Varianten dieser Ideen.

Wichtige Erkenntnisse

  • Nach Größenklassen getrennte Freispeicherlisten beschleunigen Anforderungen häufiger Größen.
  • Slab-Caches verwenden wiederkehrend benötigte Objekttypen erneut und können wiederholte Initialisierung vermeiden.
  • Der Buddy-Allokator rundet auf Zweierpotenzen auf und teilt in gleiche Hälften.
  • Der Buddy wird mit dem bereichsrelativen Offset XOR der Blockgröße bestimmt.
  • Auf jeder Ebene kann nur ein freier, gleich großer Buddy verschmolzen werden.
  • Das Aufrunden auf Zweierpotenzen nimmt interne Fragmentierung für eine einfache Verwaltung in Kauf.

Adressräume und Adressübersetzung: Beispiele

5 Interaktive Lektionen

1

Verfolge einen Zeiger durch eine private Speichersicht

8 Schritte
Verbinde den Prozessaufbau mit Übersetzung, Isolation und effizienter Ausführung.
  1. 1
    EinführungEin Rechner, mehrere Speichersichten

    Mehrere Prozesse können im physischen Speicher bleiben, während die CPU zwischen ihnen wechselt. Jeder benötigt eine stabile Sicht auf seine eigenen Befehle und Daten, auch wenn andere Prozesse starten, enden oder verschoben werden.

    Ein Adressraum stellt diese Sicht bereit. Der Zeiger, den ein Prozess verwendet, gehört zu dieser Sicht; Betriebssystem und Hardware bestimmen, welches physische Byte der Zugriff erreicht.

  2. 2
    ErklärungPlatziere die Inhalte des Programms

    Code speichert Befehle, statische Daten speichern globale und statische lokale Variablen, der Heap speichert angeforderte Blöcke und der Stack speichert den Zustand von Funktionsaufrufen.

    In der klassischen Anordnung wachsen Heap und Stack von unten beziehungsweise oben in eine Lücke hinein. Bei binären Größen bedeutet 1 KB 1.024 Bytes, auch 1 KiB genannt. Ein 16-KB-Adressraum enthält die Bytes 0 bis 16.383; seine obere Grenze ist selbst kein Byte des Adressraums.

  3. 3
    BeispielGib derselben Zahl zwei Bedeutungen

    Prozess A und Prozess B verwenden beide die virtuelle Adresse 1.000. Die Übersetzung von A kann sie zu einem physischen Byte führen, die von B zu einem anderen. Der Zahlenwert des Zeigers allein bezeichnet keinen gemeinsam genutzten Speicher.

    Ebenso zeigt die Ausgabe von &x eine virtuelle Adresse. Wird der zugrunde liegende physische Speicher verschoben, muss sich der Zeigerwert, den das Programm sieht, nicht ändern.

  4. 4
    HerausforderungDeute zwei ausgegebene Zeiger

    Zwei Prozesse geben denselben Zeigerwert aus. Müssen sie dasselbe physische Byte lesen? Erkläre, was das Ziel bestimmt.

    ---reveal---

    Nein. Jeder ausgegebene Zeiger wird im Adressraum seines eigenen Prozesses interpretiert. Die aktive Abbildung bestimmt das physische Ziel, sodass gleiche virtuelle Zahlen unterschiedliche Bytes erreichen können. Dasselbe physische Byte würde Abbildungen erfordern, die tatsächlich Speicher gemeinsam nutzen.

  5. 5
    ErklärungÜbersetze schnell und setze Grenzen durch

    Transparenz bewahrt die private Sicht des Programms. Zeiteffizienz hält den Aufwand für die Übersetzung bei jedem Abruf sowie jedem Lese- und Schreibzugriff gering. Speichereffizienz begrenzt Metadaten und verschwendete Kapazität. Schutz verhindert unberechtigte Zugriffe.

    Die Hardware übernimmt die gewöhnliche Übersetzung. Das Betriebssystem richtet die Abbildungen ein, wählt Platzierungen und übernimmt die Kontrolle, wenn ein Zugriff eine Ausnahme auslöst.

  6. 6
    HerausforderungTrenne die Entscheidung von der Operation

    Eine Speicherverwaltung wählt statt der ersten geeigneten Lücke nun die kleinste geeignete Lücke. Die Hardware prüft weiterhin die Grenze und addiert eine Basis. Hat sich der Mechanismus oder die Strategie geändert?

    ---reveal---

    Die Platzierungsstrategie hat sich geändert: Sie trifft nun eine andere Wahl zwischen Lücken. Der Übersetzungsmechanismus ist unverändert. Dieselbe Hardware kann einen Bereich übersetzen und schützen, unabhängig davon, welche geeignete Lücke das Betriebssystem gewählt hat.

  7. 7
    HerausforderungVerbinde Verschiebung und Schutz

    Ein Betriebssystem verschiebt einen angehaltenen Prozess in einen neuen physischen Bereich, erhält aber seine Zeigerwerte. Warum erfordert das sowohl Übersetzung als auch Kontrolle darüber, wer die Abbildung ändern darf?

    ---reveal---

    Die Übersetzung leitet die unveränderten virtuellen Zeiger auf die kopierten Bytes um. Nur privilegierter Betriebssystemcode darf die neue Abbildung einrichten; andernfalls könnte ein Prozess sich selbst in einen anderen Prozess oder den Kernel umlenken. Stabile Zeiger sorgen für Transparenz, während kontrollierte Abbildungen die Isolation bewahren.

  8. 8
    ZusammenfassungInterpretiere den Zeiger in seinem Prozess

    Ein Adressraum organisiert Code und Daten eines Prozesses unabhängig von der physischen Platzierung. Ein virtueller Zeiger erhält seine Bedeutung durch die Abbildung des laufenden Prozesses.

    Bewerte einen Entwurf anhand der Sicht, die er bewahrt, seiner Zeit- und Speicherkosten und der Zugriffsgrenzen, die er durchsetzt. Strategien wählen Platzierungen; Mechanismen führen Übersetzung und Prüfungen aus.

2

Verfolge eine Heap-Anforderung durch Bibliothek und Betriebssystem

12 Schritte
Verfolge Blocklebensdauern, Bibliotheksaufrufe, das Wachstum des Program Break und separate Speicherabbildungen.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungBehalte beide Zuständigkeiten im Blick
  2. 2
    BeispielUnterscheide einen Zeiger vom zugewiesenen Speicher
  3. 3
    ErklärungFordere genügend Bytes an
  4. 4
    HerausforderungSage voraus, was eine Rückkehr überdauert
  5. 5
    ErklärungGib nur einen aktiven Block frei
  6. 6
    KernaussageEin zulässiger Bereich kann einen unzulässigen Objektzugriff enthalten
  7. 7
    ErklärungVergrößere einen Bereich und teile ihn dann auf
  8. 8
    HerausforderungBestimme die neu hinzugefügten Bytes
  9. 9
    VerbindungGib einen Block frei oder entferne eine Speicherabbildung
  10. 10
    HerausforderungErkläre einen gleichbleibenden Speicherverbrauch
  11. 11
    HerausforderungVerfolge das Wachstum bei Segmentierung
  12. 12
    ZusammenfassungVerfolge Lebensdauer und Zuordnung getrennt
3

Führe einen Prozess mit Basis- und Grenzregister aus und verschiebe ihn

13 Schritte
Übersetze Zugriffe, stelle die richtige Abbildung wieder her und berechne den verschwendeten reservierten Speicher.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungBeschreibe einen physischen Platz
  2. 2
    ErklärungWende die Größenkonvention an
  3. 3
    BeispielÜbersetze Befehlsabruf und Lesezugriff
  4. 4
    HerausforderungPrüfe das letzte Byte
  5. 5
    TippErkenne die andere Grenzregisterkonvention
  6. 6
    ErklärungSchütze die Übersetzungsregister
  7. 7
    BeispielWechsle die Speichersicht mit dem CPU-Zustand
  8. 8
    HerausforderungFinde eine fehlende Wiederherstellung
  9. 9
    VerbindungVerschiebe einen Prozess, ohne Zeiger umzuschreiben
  10. 10
    BeispielBestimme die ungenutzte Spanne
  11. 11
    HerausforderungWähle die Einheit der Relokation
  12. 12
    HerausforderungBehalte die Grenze nach einer Verschiebung bei
  13. 13
    ZusammenfassungHalte Koordinaten und Kosten auseinander
4

Übersetze Code, Heap und einen nach unten wachsenden Stack

17 Schritte
Bestimme Segmentfelder, prüfe genaue Endpunkte und verbinde gemeinsame Nutzung mit physischer Platzierung.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungTeile den Prozess in logische Bereiche auf
  2. 2
    ErklärungRichte die Beispielanordnung ein
  3. 3
    BeispielBestimme den Heap-Offset
  4. 4
    HerausforderungPrüfe ein Byte außerhalb des Heaps
  5. 5
    ErklärungWähle Register mit Selektorbits aus
  6. 6
    BeispielBestimme beide Felder
  7. 7
    HerausforderungUnterscheide Kapazität und aktuelle Nutzung
  8. 8
    VerbindungLasse den Zugriff ein Segment auswählen
  9. 9
    ErklärungMiss von einer oberen Grenze nach unten
  10. 10
    BeispielBerechne einen vorzeichenbehafteten Stackversatz
  11. 11
    HerausforderungBerücksichtige das niedrigste Byte des Stacks
  12. 12
    KernaussagePrüfe die Berechtigung nach dem Finden einer gültigen Stelle
  13. 13
    ErklärungStelle mehr als eine Basis wieder her
  14. 14
    HerausforderungBetrachte Lücken getrennt
  15. 15
    VerbindungVerstehe die Grenzen der Kompaktierung
  16. 16
    HerausforderungVerschiebe ein gemeinsam genutztes Segment sicher
  17. 17
    ZusammenfassungBestimme den Bereich vor dem physischen Byte
5

Verfolge freie Blöcke und wähle Zuweisungsstrategien

18 Schritte
Verfolge Metadaten, vergleiche Platzierungsabläufe und teile und verschmelze Buddy-Blöcke.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungFrag dich, ob ein Block ausreicht
  2. 2
    BeispielBestimme, wo ungenutzte Kapazität liegt
  3. 3
    HerausforderungPrüfe, was Verschmelzen erreichen kann
  4. 4
    VerbindungErkläre, warum Zeiger auf aktive Blöcke die Kompaktierung des Heaps verhindern
  5. 5
    ErklärungBerücksichtige den Verwaltungskopf
  6. 6
    BeispielPlatziere drei Blöcke
  7. 7
    BeispielPrüfe den verbleibenden Bereich
  8. 8
    HerausforderungBestimme Verwaltungskopf und Bereich
  9. 9
    ErklärungLasse die Strategie den Block auswählen
  10. 10
    BeispielVergleiche die erste Wahl
  11. 11
    HerausforderungBlicke auf die nächste Anforderung
  12. 12
    VerbindungMache häufige Anforderungen vorhersehbar
  13. 13
    BeispielRunde einen Buddy-Bereich auf und teile ihn auf
  14. 14
    ErklärungFinde den passenden Buddy
  15. 15
    HerausforderungVerfolge die Verschmelzungsebenen
  16. 16
    KernaussageBezahle die Einfachheit innerhalb des Blocks
  17. 17
    HerausforderungTrenne Platzierung und Verschmelzen
  18. 18
    ZusammenfassungVerfolge Anordnung und Speicherbilanz

Adressräume und Adressübersetzung: Übungsaufgaben

50 Übungsfragen in 5 Sets

Set 1: Adressräume, Prüfungen und erste Speicherzuweisungen10 Übungsfragen

Frage 1 von 10

Zwei Prozesse geben jeweils einen Zeiger mit dem Zahlenwert 2.400 aus. Was sagt dir das über ihren physischen Speicher?
  1. Ihre Bytes müssen an derselben physischen Speicheradresse liegen.
  2. Ihre Abbildungen können die Bytes an verschiedenen physischen Adressen platzieren.
  3. Ihre Bytes müssen an dieser virtuellen Adresse denselben Inhalt haben.
  4. Ihre virtuellen Adressen müssen sich vor der Ausführung der Prozesse ändern.
Antwort zeigenAntwort ausblenden
Antwort: B
Jeder Zeiger wird im Adressraum seines eigenen Prozesses interpretiert, sodass gleiche virtuelle Werte unterschiedliche physische Bytes erreichen können. Gleiche Zahlen beweisen keinen gemeinsam genutzten Speicher. Dafür müssten die Abbildungen denselben zugrunde liegenden Bereich bezeichnen.

Frage 2 von 10

Ein Betriebssystem ändert, wie es einen freien Prozessplatz auswählt, behält aber dieselbe Hardware zur Grenzprüfung bei. Was hat sich geändert?
  1. Die Privilegierungsregel für den Benutzermodus hat sich geändert.
  2. Die Platzierungsstrategie hat sich geändert.
  3. Die Adressbreite des Prozesses hat sich geändert.
  4. Der Relokationsmechanismus hat sich geändert.
Antwort zeigenAntwort ausblenden
Antwort: B
Die Strategie wählt einen Platz aus den verfügbaren Alternativen. Der Mechanismus prüft weiterhin die Grenze und addiert die ausgewählte Basis genau wie zuvor. Wer dies einen neuen Relokationsmechanismus nennt, verwechselt die Platzierungsentscheidung mit den Operationen, die sie durchsetzen.

Frage 3 von 10

Ein lokaler Zeiger ist der einzige nutzbare Verweis auf einen zugewiesenen Block. Seine Funktion kehrt zurück, ohne den Block freizugeben oder den Zeiger zu speichern. Welcher Speicherfehler entsteht?
  1. Ein Speicherleck durch einen unerreichbaren zugewiesenen Block.
  2. Ein Zugriff nach Freigabe durch die Rückkehr aus der Funktion.
  3. Ein Pufferüberlauf in den nächsten Heap-Block.
  4. Eine doppelte Freigabe des früheren Blocks des Zeigers.
Antwort zeigenAntwort ausblenden
Antwort: A
Der Heap-Block bleibt zugewiesen, nachdem der lokale Zeiger verschwindet. Ohne gespeicherten Verweis kann das Programm den Block nicht über seine Zuweisungsadresse freigeben oder wiederverwenden. Die Rückkehr aus einer Funktion gibt Heap-Speicher nicht selbst frei; daher ist dies kein Zugriff nach Freigabe.

Frage 4 von 10

Ein Programm reserviert Speicher für 12 Ganzzahlen zu je 4 Bytes. Sein Zeigertyp belegt 8 Bytes. Wie viele Bytes meldet sizeof(p), wenn p die Adresse des zugewiesenen Speichers enthält?
  1. 4 Bytes
  2. 48 Bytes
  3. 12 Bytes
  4. 8 Bytes
Antwort zeigenAntwort ausblenden
Antwort: D
Der Operand von sizeof(p) ist das Zeigerobjekt, dessen Größe mit 8 Bytes angegeben ist. Der zugewiesene Speicher umfasst 48 Bytes, aber sizeof folgt keinem Zeiger, um einen Heap-Block zu messen. Wer hier die Größe des zugewiesenen Speichers verwendet, verwechselt den Zeiger mit seinem Ziel.

Frage 5 von 10

Ein Prozess hat die Basis 18.000 und eine Grenze als Größe von 3.000. Was geschieht, wenn er von der virtuellen Adresse 2.750 liest?
  1. Er liest das physische Byte 2.750.
  2. Eine Ausnahme wird ausgelöst, weil 20.750 größer als 3.000 ist.
  3. Er liest das physische Byte 20.750.
  4. Er liest das physische Byte 15.250.
Antwort zeigenAntwort ausblenden
Antwort: C
Die virtuelle Adresse 2.750 liegt unter der Grenze von 3.000 Bytes, daher ist der Zugriff zulässig. Die Addition der Basis 18.000 ergibt die physische Adresse 20.750. Ein Vergleich dieses physischen Ergebnisses mit der Grenze als Größe in Bytes würde eine Adresse fälschlich abweisen, deren virtueller Offset passt.

Frage 6 von 10

Was geschieht bei Basis 24.000 und einer Grenze als Größe von 4.500 mit einem Zugriff auf die virtuelle Adresse 4.500?
  1. Die Hardware erweitert den Bereich um ein Byte.
  2. Die Hardware greift auf das physische Byte 28.500 zu.
  3. Die Hardware weist den Zugriff außerhalb der Grenze ab.
  4. Die Hardware greift auf das physische Byte 28.499 zu.
Antwort zeigenAntwort ausblenden
Antwort: C
Ein 4.500 Byte großer Bereich enthält die virtuellen Bytes 0 bis 4.499. Die virtuelle Adresse, die der Größe entspricht, ist das erste Byte dahinter, daher löst die Hardware eine Ausnahme aus. Die Addition der Basis vor einer erfolgreichen Prüfung macht diese ausgeschlossene Grenze nicht zu einem zulässigen Byte.

Frage 7 von 10

Ein nach oben wachsender Heap beginnt virtuell bei 2.048, hat die Größe 1.536 und die physische Basis 30.000. Wohin wird die virtuelle Adresse 2.900 übersetzt?
  1. Zum physischen Byte 31.536.
  2. Zum physischen Byte 30.852.
  3. Zum physischen Byte 27.952.
  4. Zum physischen Byte 32.900.
Antwort zeigenAntwort ausblenden
Antwort: B
Der Heap-Offset beträgt 852. Du erhältst ihn, indem du den virtuellen Anfang 2.048 von der Adresse 2.900 abziehst. Er liegt unter der Größe 1.536; seine Addition zur Basis 30.000 ergibt daher 30.852. Die Addition der vollständigen virtuellen Adresse würde den virtuellen Anfang fälschlich in die Relokation einbeziehen.

Frage 8 von 10

Eine segmentierte Adresse verwendet zwei obere Selektorbits und zehn untere Offsetbits. Wie groß ist das maximale Bytefenster eines Segments?
  1. 1.024 Bytes
  2. 1.023 Bytes
  3. 4.096 Bytes
  4. 256 Bytes
Antwort zeigenAntwort ausblenden
Antwort: A
Zehn Offsetbits stellen 1.024 Bytepositionen dar, nummeriert von 0 bis 1.023. Die zwei Selektorbits wählen ein Segment, statt dessen Offsetbereich zu vergrößern. Wer alle zwölf Bits verwendet und 4.096 erhält, verwechselt den gesamten Adressraum mit dem Fenster eines Segments.

Frage 9 von 10

Ein Heap hat freie Lücken von 22 und 34 Bytes, die durch einen aktiven Block getrennt sind. Verwaltungsköpfe werden ignoriert. Warum kann eine zusammenhängende Zuweisung von 40 Bytes fehlschlagen?
  1. Keine der freien Lücken kann allein 40 Bytes aufnehmen.
  2. Beide Lücken müssen vor der Wiederverwendung verworfen werden.
  3. Die Anforderung übersteigt die gesamte freie Kapazität.
  4. Die 56 freien Bytes liegen innerhalb zugewiesener Stücke.
Antwort zeigenAntwort ausblenden
Antwort: A
Die Lücken ergeben zusammen 56 Bytes, aber der Allokator benötigt einen ununterbrochenen Bereich von 40 Bytes. Die größere Lücke ist nur 34 Bytes groß, und der aktive Block verhindert ein Verschmelzen über ihn hinweg. Das ist externe Fragmentierung; die freien Bytes liegen außerhalb zugewiesener Bereiche und sind keine interne Verschwendung.

Frage 10 von 10

Ein 512 Byte großer Heap beginnt bei 16.000. Jede Speicherzuweisung hat einen 16 Byte großen Verwaltungskopf; es wird nicht aufgerundet. First Fit weist vom unteren Ende aus zunächst 48 und dann 64 Bytes Nutzdaten zu. Der erste Block wird freigegeben. Welcher Zeiger wird für neue Nutzdaten von 56 Bytes zurückgegeben?
  1. 16.144
  2. 16.016
  3. 16.080
  4. 16.160
Antwort zeigenAntwort ausblenden
Antwort: D
Die ersten beiden Stücke belegen 64 und 80 Bytes, sodass der freie Rest bei 16.144 beginnt. Das freigegebene erste Stück umfasst insgesamt nur 64 Bytes und kann 56 Bytes Nutzdaten plus Verwaltungskopf nicht aufnehmen. Das neue Stück nutzt den Rest und gibt 16.160 zurück; 16.144 ist die Adresse seines Verwaltungskopfs.
Set 2: Prozesswechsel und Platzierungsentscheidungen10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Welcher Prozessbereich enthält in der üblichen Anordnung die Rücksprungadresse eines gewöhnlichen Funktionsaufrufs?
  1. Der Codebereich.
  2. Der Heap.
  3. Der Stack.
  4. Der statische Datenbereich.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Der Program Break liegt bei 57.344. Ein erfolgreiches sbrk(4096) erweitert den Heap. Wie lauten der Rückgabewert und der neue Program Break?
  1. Rückgabe 61.440; neuer Program Break 61.440.
  2. Rückgabe 57.344; neuer Program Break 53.248.
  3. Rückgabe 57.344; neuer Program Break 61.440.
  4. Rückgabe 4.096; neuer Program Break 61.440.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Ein Server gibt mehrere kleine Blöcke frei, aber sein vom Betriebssystem gemessener Speicherverbrauch bleibt unverändert. Wie kann das normal sein?
  1. Die Bibliothek behält die Bereiche für spätere Blockanforderungen.
  2. Die Zeiger bleiben aktiv, weil der Speicherverbrauch unverändert ist.
  3. Das Betriebssystem verzögert die Wiederverwendung der Blöcke bis zum Prozessende.
  4. Die freigegebenen Blöcke müssen jeweils noch aktiv zugewiesen sein.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Prozess R verwendet die Basis 12.000 und Prozess S die Basis 28.000, beide mit der Grenze 2.000. Nach einem Wechsel zu S erreicht ein zulässiger Zugriff auf die virtuelle Adresse 640 die Adresse 12.640. Welcher gespeicherte Zustand wurde nicht korrekt wiederhergestellt?
  1. Der Stackinhalt des bisherigen Prozesses.
  2. Das Basisregister des nächsten Prozesses.
  3. Die virtuelle Adressbreite des nächsten Prozesses.
  4. Die Grenzgröße des nächsten Prozesses.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Ein 32 KB großer Prozessplatz enthält 3 KB Code, 5 KB Heap und 4 KB Stack. Wie viel Prozent des reservierten Platzes sind ungenutzt?
  1. 62,5%
  2. 37,5%
  3. 75%
  4. 20%
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Ein nach unten wachsender Stack hat ein 4.096 Byte großes Selektorfenster, die physische Basis 60.000 und die aktuelle Größe 1.500. Sein Offsetfeld ist 3.200. Was geschieht bei einem Lesezugriff?
  1. Eine Ausnahme wird ausgelöst, weil 3.200 größer als 1.500 ist.
  2. Er erreicht das physische Byte 63.200.
  3. Er erreicht das physische Byte 58.500.
  4. Er erreicht das physische Byte 59.104.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Zwei Prozesse nutzen ein Codesegment mit Lese- und Ausführungsberechtigung an derselben physischen Basis gemeinsam. Einer versucht, innerhalb der Grenzen hineinzuschreiben. Was sollte geschehen?
  1. Der Code beider Prozesse ändert sich an diesem Byte.
  2. Nur die private Codekopie des schreibenden Prozesses ändert sich.
  3. Eine Berechtigungsausnahme verhindert den Schreibzugriff.
  4. Der Schreibzugriff gelingt, weil sein Offset zulässig ist.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Freie Lücken sind in Adressreihenfolge 44, 18, 70 und 32 Bytes groß. Verwaltungsköpfe werden ignoriert; jede Zuweisung nimmt das untere Ende. Welche Strategie bedient Anforderungen von zuerst 30 und dann 65 Bytes, wenn zwischen den Anforderungen keiner der Blöcke freigegeben wird: First Fit, Best Fit oder Worst Fit?
  1. First Fit und Worst Fit sind erfolgreich; Best Fit schlägt fehl.
  2. Best Fit und Worst Fit sind erfolgreich; First Fit schlägt fehl.
  3. Alle drei Strategien bedienen beide Anforderungen erfolgreich.
  4. First Fit und Best Fit sind erfolgreich; Worst Fit schlägt fehl.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Next Fit findet Lücken von 16, 48, 24 und 40 Bytes vor. Die Suche beginnt bei der vierten Lücke und setzt sich bei Bedarf am Anfang fort. Verwaltungsköpfe werden ignoriert. Welche Lücke bedient eine Anforderung von 30 Bytes?
  1. Die dritte Lücke mit 24 Bytes.
  2. Die zweite Lücke mit 48 Bytes.
  3. Die erste Lücke mit 16 Bytes.
  4. Die vierte Lücke mit 40 Bytes.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Ein Buddy-Allokator beginnt mit einem freien 32-KB-Bereich und ignoriert Verwaltungsköpfe. Er weist zwei Anforderungen von je 6 KB an den niedrigsten verfügbaren Offsets zu und gibt dann die erste frei. Wie groß ist der unmittelbar danach größte verfügbare Block?
  1. 24 KB
  2. 16 KB
  3. 32 KB
  4. 8 KB
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Set 3: Grenzen und Allokatorzustand verfolgen10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Ein System prüft jede virtuelle Adresse korrekt, wechselt aber bei jedem zulässigen Lesezugriff in Kernelcode. Welches Virtualisierungsziel wird am unmittelbarsten geschwächt?
  1. Transparenz der Platzierung.
  2. Speichereffizienz.
  3. Prozessisolation.
  4. Zeiteffizienz.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Ein 16 Byte großer Heap-Block wird freigegeben. Anschließend wird ein alter Zeiger verwendet, um innerhalb des weiterhin abgebildeten Heaps desselben Prozesses zu schreiben, ohne dass sofort ein Fehler auftritt. Was beschreibt den Schreibzugriff am besten?
  1. Eine doppelte Freigabe, weil die Adresse erneut verwendet wird.
  2. Ein gültiger Schreibzugriff, weil der Heap abgebildet bleibt.
  3. Ein Zugriff nach Freigabe mit undefiniertem Verhalten.
  4. Ein Speicherleck, weil der Block nie freigegeben wurde.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

In einem Segmentierungsmodell belegt ein Heap die physischen Bytes 10.000 bis 13.999. Seine nächsten 1.000 physischen Bytes sind belegt. Freie Lücken an anderer Stelle sind 3.000 und 4.500 Bytes groß. Der Allokator muss diesen Heap auf 5.000 Bytes vergrößern. Kann eine direkte Erweiterung oder Relokation dies ermöglichen?
  1. Eine Relokation in die größte Lücke mit 4.500 Bytes gelingt.
  2. Keiner der beiden Wege bietet einen geeigneten zusammenhängenden Bereich.
  3. Eine direkte Erweiterung gelingt, weil 1.000 zusätzliche Bytes benötigt werden.
  4. Eine Relokation gelingt, weil die Lücken zusammen 7.500 Bytes ergeben.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Ein Prozess, dessen Grenzregister eine Größe enthält, belegt die physischen Bytes 36.000 bis 40.095. Ein Befehl an der virtuellen Adresse 3.900 versucht, von der virtuellen Adresse 4.096 zu laden. Was geschieht?
  1. Der Abruf gelingt bei 39.900; der Lesezugriff löst eine Ausnahme aus.
  2. Beide Zugriffe lösen Ausnahmen aus; beide überschreiten die Größengrenze.
  3. Beide Zugriffe gelingen im zugewiesenen Bereich.
  4. Der Abruf gelingt bei 39.900; der Lesezugriff nutzt 40.096.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Ein Betriebssystem kopiert einen angehaltenen Prozess von der Basis 22.000 zur Basis 47.000, ohne seine Grenze zu ändern. Wohin sollte sein virtueller Zeiger 960 nach der Fortsetzung führen?
  1. Zum physischen Byte 22.960.
  2. Zum physischen Byte 69.960.
  3. Zum physischen Byte 25.960.
  4. Zum physischen Byte 47.960.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Eine virtuelle 14-Bit-Adresse verwendet zwei Selektorbits und zwölf Offsetbits. Der Selektor 01 bezeichnet einen nach oben wachsenden Heap mit der Basis 52.000 und der Größe 900. Was geschieht bei einem Lesezugriff auf die virtuelle Adresse 0x1380?
  1. Eine Ausnahme wird ausgelöst, weil 0x1380 größer als 900 ist.
  2. Er erreicht das physische Byte 52.896.
  3. Er erreicht das physische Byte 52.900.
  4. Er erreicht das physische Byte 56.992.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Ein Betriebssystem hat freie Lücken von 5 KB, 11 KB und 7 KB, die durch zugewiesene Segmente getrennt sind. Es muss ein 13 KB großes Segment platzieren, ohne aktive Segmente zu verschieben. Warum kann die Anforderung fehlschlagen?
  1. Jedes Segment muss denselben Platz fester Größe nutzen.
  2. Die größte vorhandene Lücke ist nur 11 KB groß.
  3. Die freien Lücken ergeben zusammen weniger als 13 KB.
  4. Das Segment muss zuerst die kleinste Lücke nutzen.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Ein 1.024 Byte großer Heap bei 8.000 verwendet 16 Byte große Verwaltungsköpfe und 16 Byte große freie Knoten. Jeder freie Knoten erfasst die nutzbaren Bytes nach sich selbst. Zwei Nutzdatenbereiche von je 80 Bytes werden vom unteren Ende aus zugewiesen. Wie lauten Adresse und nutzbare Größe des verbleibenden freien Knotens?
  1. Adresse 8.192; nutzbare Größe 816 Bytes.
  2. Adresse 8.192; nutzbare Größe 832 Bytes.
  3. Adresse 8.160; nutzbare Größe 848 Bytes.
  4. Adresse 8.224; nutzbare Größe 800 Bytes.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Ein Kernel weist wiederholt denselben Objekttyp zu, und die Initialisierung dieses Typs ist aufwendig. Welcher Entwurf unterstützt unmittelbar die Wiederverwendung initialisierter Objekte?
  1. Ein Slab-Objektcache.
  2. Ein Next-Fit-Suchzeiger.
  3. Ein Grenzregister mit Größenangabe.
  4. Ein statisches Relokationsprogramm.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Wie lautet in einem Buddy-Bereich der Buddy-Offset eines 8-KB-Blocks beim bereichsrelativen Offset 24 KB?
  1. 8 KB
  2. 0 KB
  3. 16 KB
  4. 32 KB
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Set 4: Fehler erklären und Abbildungen bewahren10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Eine Funktion enthält einen statischen lokalen Zähler und einen gewöhnlichen automatischen lokalen Zähler. Der Speicher welches Zählers bleibt über aufeinanderfolgende Aufrufe hinweg bestehen?
  1. Der Speicher beider lokaler Zähler.
  2. Der Speicher keines lokalen Zählers.
  3. Der Speicher des statischen lokalen Zählers.
  4. Der Speicher des automatischen lokalen Zählers.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Eine C-Zeichenkette enthält 23 sichtbare Zeichen von je einem Byte. Ein Programm reserviert genau 23 Bytes und kopiert die Zeichen gefolgt von einem abschließenden Nullzeichen. Was geht schief?
  1. Das erste Zeichen wird vor seiner Initialisierung gelesen.
  2. Der Zeiger selbst benötigt 24 Bytes Speicher.
  3. Der Allokator erhält eine ungültige Freigabeanforderung.
  4. Das abschließende Nullzeichen wird über den Block hinaus geschrieben.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Zwei Prozesse reservieren jeweils einen 24 KB großen Platz für das Verfahren mit Basis- und Grenzregister. Jeder nutzt 2 KB Code, 3 KB Heap und 1 KB Stack. Wie viel physischen Speicher verbrauchen ihre reservierten Plätze zusammen?
  1. 12 KB
  2. 48 KB
  3. 24 KB
  4. 36 KB
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Ein physischer Speichervorrat von 48 KB enthält drei Prozesse. Jeder hat eine virtuelle Spanne von 16 KB, nutzt aber derzeit zusammenhängende Bereiche für Code, Heap und Stack von insgesamt 5 KB. Ein vierter identischer Prozess kommt hinzu. Können das Verfahren mit Basis- und Grenzregister für den gesamten Adressraum und die getrennte Segmentierung ihn aufnehmen, wenn Metadaten und Fragmentierung ignoriert werden?
  1. Das Verfahren mit Basis- und Grenzregister kann es nicht; Segmentierung kann es.
  2. Beide Verfahren können den vierten Prozess aufnehmen.
  3. Segmentierung kann es nicht; das Verfahren mit Basis- und Grenzregister kann es.
  4. Keines der beiden Verfahren kann den vierten Prozess aufnehmen.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Ein nach oben wachsender Heap beginnt virtuell bei 8.192, hat die Größe 2.000 und die physische Basis 40.000. Was geschieht bei einem Lesezugriff auf die virtuelle Adresse 10.192?
  1. Er erreicht das physische Byte 50.192.
  2. Er erreicht das physische Byte 41.999.
  3. Er löst am ausgeschlossenen Segmentende eine Ausnahme aus.
  4. Er erreicht das physische Byte 42.000.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Ein nach oben wachsender Heap beginnt virtuell bei 4.096 und hat die Größe 1.500. Seine physische Basis ändert sich nach einer Relokation von 20.000 auf 46.000. Eine Ablaufverfolgung zeigt anschließend, dass die virtuelle Adresse 5.300 die physische Adresse 51.300 erreicht. Welche Korrektur ist nötig?
  1. Verwende die physische Adresse 47.204 nach Abzug des virtuellen Heap-Anfangs.
  2. Verwende die physische Adresse 21.204 durch Wiederherstellen der ursprünglichen Basis.
  3. Behalte die physische Adresse 51.300, weil die Relokation die vollständige Adresse ändert.
  4. Verwende die physische Adresse 50.096, indem du den Offset durch den Anfang ersetzt.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Eine 14-Bit-Adresse verwendet zwei Selektorbits und zwölf Offsetbits. Der Selektor 11 bezeichnet einen nach unten wachsenden Stack mit der physischen Basis 70.000 und der aktuellen Größe 1.024. Lesezugriffe sind erlaubt. Was geschieht bei der virtuellen Adresse 15.360?
  1. Sie erreicht durch Addition des positiven Felds die physische Adresse 73.072.
  2. Sie erreicht das niedrigste Stackbyte bei der physischen Adresse 68.976.
  3. Sie löst eine Ausnahme aus, weil der Betrag des Versatzes der Größe entspricht.
  4. Sie erreicht die physische Adresse 68.975 als letztes erlaubtes Byte.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Ein Allokator rundet jede Nutzdatenanforderung auf ein Vielfaches von 32 Bytes auf und ignoriert Verwaltungsköpfe. Welche Art und Menge an Verschwendung erzeugt eine Anforderung von 70 Bytes?
  1. 32 Bytes externe Fragmentierung.
  2. 6 Bytes interne Fragmentierung.
  3. 26 Bytes externe Fragmentierung.
  4. 26 Bytes interne Fragmentierung.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Ein 100 Byte großer Heap hat aktive Stücke bei den Bytes 20–39 und 60–79; alle anderen Bytes sind frei. Das erste aktive Stück wird freigegeben und benachbarte freie Blöcke verschmelzen. Kann eine Anforderung von 65 Bytes danach erfolgreich sein, wenn Metadaten ignoriert werden?
  1. Nein; der größte freie Bereich ist 40 Bytes groß.
  2. Ja; insgesamt sind 80 Bytes frei.
  3. Ja; die Freigabe eines Stücks verbindet den gesamten Heap.
  4. Nein; der größte freie Bereich ist 60 Bytes groß.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Lücken sind in Adressreihenfolge 26, 58 und 42 Bytes groß. Verwaltungsköpfe werden ignoriert. First Fit und Best Fit weisen jeweils erst 24 und dann 40 Bytes zu, ohne Freigaben. Welche Strategie kann danach eine Anforderung von 55 Bytes bedienen?
  1. Keine der beiden kann die Anforderung von 55 Bytes bedienen.
  2. First Fit kann es; Best Fit kann es nicht.
  3. Best Fit kann es; First Fit kann es nicht.
  4. Beide können die Anforderung von 55 Bytes bedienen.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Set 5: Adressübersetzung und Speicherzuweisung gemeinsam analysieren10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Ein Entwurf gibt zwei Prozessen einen gemeinsamen physischen Codebereich ohne Schreibberechtigung und getrennte beschreibbare Bereiche. Welche Virtualisierungsziele kann dies erfüllen und dabei Speicher sparen?
  1. Transparenz erfordert gemeinsam beschreibbaren Code für beide.
  2. Speichereffizienz erfordert das Entfernen aller Zugriffsprüfungen.
  3. Transparenz und Isolation können beide erhalten bleiben.
  4. Isolation bleibt nur durch getrennte Codekopien erhalten.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Ein segmentierter Heap belegt die physischen Bytes 20.000 bis 23.999, gefolgt von belegtem Speicher. Sein Program Break liegt virtuell bei 10.000. Das Betriebssystem verschiebt den Heap in einen freien 6.000 Byte großen Bereich bei der physischen Adresse 40.000 und erfüllt sbrk(1500) erfolgreich. Was gibt sbrk zurück und wo liegt nun das erste bereits vorhandene Byte des Heaps?
  1. Rückgabe 11.500; vorhandenes erstes Byte bei 40.000.
  2. Rückgabe 10.000; vorhandenes erstes Byte bei 40.000.
  3. Rückgabe 41.500; vorhandenes erstes Byte bei 40.000.
  4. Rückgabe 10.000; vorhandenes erstes Byte bei 20.000.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Ein Programm im Benutzermodus versucht, sein Basisregister auf den physischen Anfang des Kernels zu setzen. Was sollte der Prozessor tun?
  1. Die angeforderte Basis als gewöhnlichen virtuellen Zeiger behandeln.
  2. Die Basis setzen und sich auf die bestehende Größenprüfung verlassen.
  3. Für die privilegierte Operation eine Ausnahme auslösen.
  4. Die Basis nur für den nächsten Lesezugriff dieses Programms setzen.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Eine 14-Bit-Adresse hat zwei Selektorbits und zwölf Offsetbits. Der Selektor 01 bezeichnet einen nach oben wachsenden Heap mit der Basis 26.000 und der Größe 1.200. Seine Größe wächst später auf 1.400. Wie verändert sich ein Lesezugriff auf die virtuelle Adresse 0x1500?
  1. Statt einer Ausnahme erreicht er das physische Byte 27.280.
  2. Er bleibt zulässig und erreicht das physische Byte 27.280.
  3. Er löst weiterhin eine Ausnahme aus, weil sich der Selektor ändert.
  4. Statt einer Ausnahme erreicht er das physische Byte 31.376.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Welche Abbildungsdaten muss das Betriebssystem für die verschobenen Segmente korrigieren, nachdem es Prozesse angehalten und ihre physischen Segmente kompaktiert hat?
  1. Ihre physischen Basen.
  2. Ihre virtuellen Zeiger.
  3. Ihre Zugriffsrechte.
  4. Ihre Code-Offsets.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Der physische Speicher enthält das aktive Segment A bei den Bytes 0 bis 3.999, eine freie Lücke bei den Bytes 4.000 bis 5.999, das aktive Segment B bei den Bytes 6.000 bis 8.999 und eine freie Lücke bei den Bytes 9.000 bis 11.999. B darf verschoben werden. Wohin kann B verschoben werden, damit danach ein neues 4.500 Byte großes Segment hineinpasst?
  1. Verschiebe B zu den Bytes 5.000 bis 7.999.
  2. Verschiebe B zu den Bytes 8.000 bis 10.999.
  3. Verschiebe B zu den Bytes 7.000 bis 9.999.
  4. Verschiebe B zu den Bytes 4.000 bis 6.999.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Ein 640 Byte großer Heap beginnt bei 24.000. Speicherzuweisungen haben 16 Byte große Verwaltungsköpfe und werden nicht aufgerundet. Nutzdaten von 64, 96 und 64 Bytes werden vom unteren Ende aus nacheinander zugewiesen. Die ersten beiden werden freigegeben und verschmolzen. First Fit bedient dann Nutzdaten von 144 Bytes. Welcher Zeiger und welcher gesamte freie Rest innerhalb des verschmolzenen Stücks ergeben sich?
  1. Zeiger 24.016; Rest 32 Bytes.
  2. Zeiger 24.016; Rest 48 Bytes.
  3. Zeiger 24.096; Rest 32 Bytes.
  4. Zeiger 24.000; Rest 48 Bytes.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Lücken sind in Adressreihenfolge 36, 64 und 48 Bytes groß. Verwaltungsköpfe werden ignoriert. Zuerst kommt eine Anforderung von 32 Bytes, dann eine von 60 Bytes. Next Fit beginnt seine erste Suche bei der zweiten Lücke und setzt sie bei deren Rest fort. Welche Strategie kann beide Anforderungen bedienen: Best Fit oder dieses Next Fit?
  1. Keine der beiden Strategien kann beide Anforderungen bedienen.
  2. Best Fit kann es; Next Fit kann es nicht.
  3. Beide Strategien können beide Anforderungen bedienen.
  4. Next Fit kann es; Best Fit kann es nicht.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Ein Buddy-Allokator ignoriert den Verwaltungsaufwand in einem 32-KB-Bereich. Er weist 7 KB bei Offset 0, weitere 7 KB bei Offset 8 KB und 12 KB bei Offset 16 KB zu. Beide 7-KB-Zuweisungen werden freigegeben. Warum kann er eine neue Anforderung von 20 KB nicht erfüllen?
  1. Die beiden freigegebenen Zuweisungen liefern nur 14 KB und können daher keinen 16-KB-Block bilden.
  2. Der benötigte 32-KB-Block kann nicht entstehen, solange sein oberer 16-KB-Buddy belegt ist.
  3. Dem verfügbaren 16-KB-Block fehlen nur 4 KB zur benötigten Blockgröße.
  4. Die unteren freien Blöcke bleiben getrennt, weil das Verschmelzen eine neue Teilung benötigt.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Ein Prozess mit Basis- und Grenzregister hat die physische Basis 11.500. Welche physische Adresse entspricht seiner zulässigen virtuellen Adresse 700?
  1. 10.800
  2. 700
  3. 11.500
  4. 12.200
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Hängst du bei deiner eigenen Aufgabe fest? Hausaufgabenhilfe

Schlüsselbegriffe

73 Schlüsselbegriffe, jeweils mit Definition

A

Adressraum
Ein Adressraum ist die einem laufenden Prozess verfügbare Sicht auf den Speicher einschließlich der Bereiche für Befehle, Daten, Heap-Blöcke und Funktionsaufrufe.
Zwei Prozesse können denselben Adressraumaufbau verwenden, während ihre Bytes unterschiedliche physische Bereiche belegen.
Aktive Speicherbelegung
Eine aktive Speicherbelegung ist ein Block, der noch dem Programm zugeordnet und nicht freigegeben ist und dessen zurückgegebene Adresse ein gewöhnlicher C-Allokator erhalten muss.
Ein aktiver Block zwischen freien Lücken verhindert, dass der Allokator diese über ihn hinweg verschmilzt.
Anonyme Speicherabbildung
Eine anonyme Speicherabbildung ist ein mit Nullen gefüllter virtueller Speicherbereich, dessen Speicher ohne Zuordnung zu einer Datei erzeugt wird.
Ein Allokator kann durch eine anonyme mmap-Anforderung einen großen separaten Bereich erhalten.
Arena
Eine Arena ist ein getrennt verwalteter Allokatorbereich, der neben anderen Arenen genutzt wird, um die Konkurrenz um eine gemeinsame Zuweisungssperre zu verringern.
Threads, die unterschiedliche Arenen nutzen, können viele Anforderungen bedienen, ohne auf dieselbe Allokatorsperre zu warten.
Aufrundung der Zuweisungsgröße
Die Aufrundung der Zuweisungsgröße erhöht eine angeforderte Größe auf eine unterstützte Größe oder Ausrichtung, sodass überschüssige Kapazität innerhalb des zugewiesenen Blocks bleibt.
Das Aufrunden von 21 Bytes auf einen 32-Byte-Block lässt 11 Bytes interne Verschwendung zurück.
Ausnahmebehandlungsroutine
Eine Ausnahmebehandlungsroutine ist Betriebssystemcode, der aufgerufen wird, nachdem die Hardware ein Ereignis wie eine ungültige Speicheradresse oder einen verbotenen Befehl erkannt hat.
Eine Grenzverletzung übergibt die Kontrolle an die registrierte Behandlungsroutine, die den Prozess üblicherweise beendet.

B

Basisregister
Ein Basisregister enthält bei gewöhnlicher Relokation nach oben die physische Anfangsadresse eines Bereichs, zu der gültige virtuelle Offsets addiert werden.
Bei der physischen Basis 12.000 erreicht der virtuelle Offset 200 nach erfolgreicher Prüfung das physische Byte 12.200.
Best-Fit-Verfahren
Das Best-Fit-Verfahren wählt den kleinsten freien Block, der für eine Anforderung groß genug ist. Das begrenzt den unmittelbaren Rest, erzeugt aber manchmal winzige Reste.
Bei Lücken von 60, 28 und 44 Bytes nimmt eine Anforderung von 25 Bytes mit Best Fit die 28-Byte-Lücke.
Bitmaske
Eine Bitmaske wählt durch eine bitweise Operation bestimmte Bitpositionen aus und erlaubt Hardware oder Code so, Adressfelder zu trennen.
Die Anwendung von va & 0x0FFF behält zwölf untere Bits und setzt die beiden Segmentbits einer 14-Bit-Adresse auf null.
Blockaufteilung
Blockaufteilung teilt einen freien Block in einen zugewiesenen und einen verbleibenden freien Teil und erhält den Rest für spätere Anforderungen.
Eine Anforderung von 24 Bytes aus einem 80-Byte-Block lässt 56 Bytes übrig, wenn Verwaltungsköpfe ignoriert werden.
brk
brk setzt die Endgrenze des traditionellen Heaps auf eine angegebene Adresse und erlaubt dem Allokator so, die Größe dieses Bereichs zu ändern.
Der Allokator verwaltet den Program Break, daher sollte Anwendungscode Blöcke über die Bibliothek anfordern.
Buddy
Ein Buddy ist der gleich große Partner, der durch die Teilung eines übergeordneten Blocks entsteht. Er lässt sich bestimmen, indem der Offset seines Partners durch XOR mit der Blockgröße verknüpft wird.
Zwei benachbarte 8-KB-Blöcke können nur verschmelzen, wenn sie die Partner einer 16-KB-Teilung sind.
Buddy-Allokator
Ein Buddy-Allokator verwaltet Blöcke mit Zweierpotenzgrößen, teilt sie in gleiche Hälften und verschmilzt einen freigegebenen Block nur mit seinem passenden freien Buddy.
Eine Anforderung von 10 KB erhält einen 16-KB-Block und lässt 6 KB innerhalb dieser Zuweisung ungenutzt.

C

calloc
calloc ist eine Speicherverwaltungsfunktion der C-Bibliothek, die mit Nullbytes gefüllten Speicher bereitstellt, anders als der nicht gelöschte Speicher, den malloc zurückgibt.
Ein über calloc erhaltener Puffer enthält zunächst Nullbytes, bevor das Programm Daten schreibt.

D

Doppelte Speicherfreigabe
Eine doppelte Speicherfreigabe ist der Versuch, denselben zugewiesenen Speicher erneut freizugeben, nachdem er bereits freigegeben wurde. Das verletzt die Zuordnungsregeln des Allokators.
Zwei Aufrufe von free über kopierte Zeiger auf denselben Block können den Zustand der Speicherverwaltungsbibliothek beschädigen.
Dünn besetzter Adressraum
Ein dünn besetzter Adressraum hat große ungenutzte virtuelle Lücken zwischen genutzten Bereichen, sodass die physische Zuweisung der gesamten Spanne verschwenderisch ist.
Werden Heap und Stack in unabhängige Segmente getrennt, muss ihre leere Zwischenlücke nicht reserviert werden.
Dynamische Relokation
Dynamische Relokation übersetzt Adressen während der Ausführung und erlaubt dem Betriebssystem, physischen Speicher zu verschieben und virtuelle Zeigerwerte dabei zu erhalten.
Wird ein angehaltener Prozess verschoben und seine Basis geändert, erreichen seine bestehenden Zeiger die kopierten Bytes.

E

Eingebetteter Freispeicherlistenknoten
Ein eingebetteter Freispeicherlistenknoten speichert Größe und Verweise eines freien Blocks innerhalb des Blocks selbst und verwendet verwalteten Speicher für Verwaltungsdaten.
Nach der Aufteilung eines freien Blocks enthalten die ersten Bytes seines freien Rests dessen Knoten, statt dafür Speicher außerhalb des Heaps zuzuweisen.
Externe Fragmentierung
Externe Fragmentierung lässt freien Speicher trotz ausreichender Gesamtkapazität in getrennten Lücken zurück, die für eine zusammenhängende Anforderung zu klein sind.
Lücken von 7 KB und 9 KB können kein einzelnes 12-KB-Segment aufnehmen, obwohl sie zusammen 16 KB ergeben.

F

Feingranulare Segmentierung
Feingranulare Segmentierung teilt einen Adressraum in viele kleinere, unabhängig geschützte Bereiche, die durch eine Segmenttabelle beschrieben werden.
Eine einzelne Datenstruktur kann in einem feingranularen Entwurf eigene Regeln für gemeinsame Nutzung und Zugriff haben.
First-Fit-Verfahren
Das First-Fit-Verfahren wählt den ersten ausreichenden Block in Listenreihenfolge und beendet die Suche. Seine Platzierung hängt daher von der Reihenfolge der Liste ab.
Bei nach Adressen sortierten Lücken von 60, 28 und 44 Bytes nutzt eine Anforderung von 25 Bytes die 60-Byte-Lücke.
free
free ist eine C-Bibliotheksfunktion, die einen aktiven zugewiesenen Block an den Allokator zurückgibt; die Übergabe von NULL ist erlaubt und hat keine Wirkung.
Der Allokator kann einen freigegebenen Block wiederverwenden, selbst wenn der vom Betriebssystem gemessene Speicherverbrauch des Prozesses gleich bleibt.
Freispeicherliste
Eine Freispeicherliste erfasst verfügbare Blöcke und ihre Größen. Verweise oder eine andere Struktur erlauben dem Allokator, Einträge zu finden und zu aktualisieren.
Nach der Aufteilung eines Blocks bleibt sein ungenutzter Rest auf der Freispeicherliste, wenn er einen nutzbaren freien Knoten aufnehmen kann.
Freispeicherverwaltung
Freispeicherverwaltung erfasst verfügbare Blöcke und wählt aus, wo Anforderungen platziert werden, auf Ebene eines Prozessheaps oder physischer Betriebssystembereiche.
Ein Allokator muss einen ausreichend großen Block finden, selbst wenn seine gesamte freie Byteanzahl die Anforderung übersteigt.

G

Gemeinsame Codenutzung
Gemeinsame Codenutzung erlaubt Prozessen, eine physische Kopie unveränderlicher Befehle auszuführen und dabei ihren beschreibbaren Prozesszustand getrennt zu halten.
Zwei Prozesse können dieselbe Codebasis ohne Schreibberechtigung mit unterschiedlichen Heap- und Stackbasen verwenden.
Grenzregister
Ein Grenzregister begrenzt einen Prozessbereich und enthält in diesem Modell dessen Größe in Bytes; gültige virtuelle Adressen müssen strikt kleiner als diese Größe sein.
Die Grenze 4.096 erlaubt das virtuelle Byte 4.095, weist aber einen Zugriff auf die virtuelle Adresse 4.096 ab.
Grobgranulare Segmentierung
Grobgranulare Segmentierung teilt einen Adressraum in einige große Bereiche, üblicherweise Code, Heap und Stack, mit jeweils eigenem Übersetzungszustand.
Eine kleine Gruppe von Segmentregisterpaaren kann den klassischen Prozessaufbau mit drei Bereichen beschreiben.

H

Hängender Zeiger
Ein hängender Zeiger ist ein Zeiger auf Speicher, dessen Lebensdauer abgelaufen ist, etwa auf einen bereits an den Allokator zurückgegebenen Heap-Block.
Das Lesen über einen alten Zeiger nach der Freigabe seines Blocks ist ein Fehler durch Zugriff nach Freigabe.
Heap
Der Heap ist ein Prozessbereich für Blöcke, die während der Ausführung angefordert werden und deren Lebensdauer durch Zuweisung und Freigabe verwaltet wird.
Ein Objekt auf dem Heap kann die Funktion überdauern, die seinen Speicher ursprünglich angefordert hat.

I

Implizite Segmentauswahl
Die implizite Segmentauswahl wählt den Übersetzungszustand anhand der Art, wie eine Adresse gebildet wurde, etwa durch einen Befehlsabruf oder die Verwendung des Stapelzeigers.
Ein Befehlsabruf verwendet die Codeübersetzung, ohne ein explizites Segmentnummernfeld in seiner Adresse zu benötigen.
Interne Fragmentierung
Interne Fragmentierung ist ungenutzte Kapazität innerhalb eines zugewiesenen Bereichs. Sie entsteht, wenn mehr Platz reserviert wird, als sein nützlicher Inhalt benötigt.
Ein 16 KB großer Prozessplatz mit 4 KB nützlichem Inhalt verschwendet innerhalb des zugewiesenen Bereichs 12 KB.
Isolation
Isolation ist die durchgesetzte Trennung zwischen Prozessen, sodass ein unberechtigter Zugriff einen anderen Prozess oder den Kernel weder lesen noch beschädigen kann.
Die Hardware weist den Zugriff eines Editors außerhalb seines erlaubten Bereichs ab, bevor er einen benachbarten Prozess beschädigt.

K

Kompaktierung
Kompaktierung verschiebt aktive physische Bereiche aneinander und aktualisiert ihre Abbildungen, um verstreute freie Kapazität zu einer größeren Lücke zusammenzuführen.
Das Betriebssystem kopiert Segmente und ändert ihre Basen, während Programme weiterhin dieselben virtuellen Adressen verwenden.
Kontextwechsel
Ein Kontextwechsel ersetzt den aktiven CPU-Zustand des laufenden Prozesses durch den gespeicherten Zustand eines anderen Prozesses einschließlich seiner Werte für die Speicherübersetzung.
Werden die allgemeinen Register des nächsten Prozesses ohne seine Basis wiederhergestellt, bleibt die falsche Speichersicht aktiv.

L

Lese- und Ausführungsberechtigung
Die Lese- und Ausführungsberechtigung erlaubt, die Bytes eines Bereichs zu lesen und als Befehle auszuführen, während verändernde Schreibzugriffe verboten sind.
Ein gemeinsam genutztes Codesegment erlaubt Befehlsabrufe, weist aber einen versuchten Schreibzugriff auf seine Bytes ab.
Lesen nicht initialisierter Daten
Beim Lesen nicht initialisierter Daten werden gespeicherte Daten verwendet, bevor das Programm ihren Wert festgelegt hat, etwa beim Lesen nicht gelöschter Bytes von malloc.
Genügend Speicher für eine Ganzzahl zu reservieren, gibt dieser Ganzzahl noch keinen aussagekräftigen Anfangswert.

M

malloc
malloc ist eine C-Bibliotheksfunktion, die einen Block einer angegebenen Größe in Bytes anfordert und dessen Adresse oder bei Fehlschlag NULL zurückgibt, ohne seinen Inhalt zu löschen.
Eine Anforderung für sechs Ganzzahlen verwendet malloc(6 * sizeof(int)), um die Elementgröße zu berücksichtigen.
Mechanismus
Ein Mechanismus ist die Implementierung, die eine Operation ausführt, etwa wenn die Hardware bei Speicherzugriffen Grenzen prüft und eine Basis addiert.
Die Änderung der Regel zur Lückenauswahl erfordert keinen Austausch des Hardwaremechanismus zur Übersetzung.
munmap
munmap entfernt einen abgebildeten Bereich aus dem Adressraum eines Prozesses, anders als die Rückgabe eines kleinen Blocks an einen Bibliotheksvorrat zur Wiederverwendung.
Die Freigabe eines separat abgebildeten großen Blocks kann es dem Allokator ermöglichen, die zugrunde liegende Speicherabbildung zu entfernen.

N

Nach Größenklassen getrennte Freispeicherlisten
Nach Größenklassen getrennte Freispeicherlisten halten separate Vorräte für ausgewählte Blockgrößen, sodass Anforderungen häufiger Größen ohne allgemeine Suche bedient werden können.
Ein Vorrat von 64-Byte-Blöcken bedient wiederholte Anforderungen dieser Größenklasse schnell.
Negativer Versatz
Ein negativer Versatz misst einen Zugriff auf ein nach unten wachsendes Segment unterhalb seiner oberen Grenze und wird durch Abziehen der vollen Fenstergröße berechnet.
Ein Feldwert von 3.000 in einem 4.096 Byte großen Stackfenster ergibt einen Versatz von −1.096.
Next-Fit-Verfahren
Das Next-Fit-Verfahren sucht ab einer gespeicherten Position nach einem ausreichenden Block und setzt die Suche bei Bedarf am Listenanfang fort, statt dort neu zu beginnen.
Eine Next-Fit-Suche bei der dritten statt der ersten Lücke einer Liste zu beginnen, kann die Wahl selbst bei unveränderten freien Größen ändern.
Nutzdaten
Nutzdaten sind der Teil eines zugewiesenen Blocks, der dem Programm zur Verfügung steht, ohne den Verwaltungskopf und andere Verwaltungsdaten des Allokators.
Nutzdaten von 96 Bytes belegen mit einem sechzehn Byte großen Verwaltungskopf mindestens 112 Bytes des verwalteten Bereichs.

O

Offsetfeld
Ein Offsetfeld ist der Teil einer segmentierten Adresse, der ein Byte innerhalb eines Selektorfensters bestimmt, bevor eine Anpassung für Wachstum nach unten erfolgt.
Ein zwölf Bit großes Feld erlaubt 4.096 Positionen, selbst wenn das ausgewählte Segment derzeit weniger Bytes nutzt.

P

Physische Adresse
Eine physische Adresse bezeichnet eine Stelle in der Speicherhardware, die nach der Übersetzung eines virtuellen Speicherzugriffs eines Prozesses erreicht wird.
Die virtuelle Adresse 800 zweier Prozesse kann auf demselben Rechner in unterschiedliche physische Adressen übersetzt werden.
Privilegierter Befehl
Ein privilegierter Befehl ist eine Operation, die nur im Kernelmodus erlaubt ist und so verhindert, dass Benutzerprogramme geschützten Rechnerzustand ändern.
Der Versuch, das Basisregister im Benutzermodus zu ändern, löst eine Ausnahme aus, statt die Abbildung zu ändern.
Program Break
Der Program Break ist die virtuelle Grenze unmittelbar hinter dem Ende des traditionellen Heaps. Sie wird verschoben, wenn dieser Heap-Bereich erweitert oder verkleinert wird.
Liegt der Program Break bei 30.000, befindet sich Byte 30.000 außerhalb des Heaps, bis die Grenze nach oben verschoben wird.
Prozesskontrollblock
Ein Prozesskontrollblock ist ein Datensatz des Betriebssystems mit dem gespeicherten Ausführungszustand eines Prozesses, einschließlich der Übersetzungswerte, wenn der Prozess inaktiv ist.
Das Betriebssystem stellt die gespeicherte Basis und Grenze des nächsten Prozesses aus dessen Prozesskontrollblock wieder her.
Pufferüberlauf
Ein Pufferüberlauf ist ein Schreibzugriff über das zugewiesene Ende eines Puffers hinaus, der benachbarte Daten oder Verwaltungsdaten des Allokators beschädigen kann.
Das Kopieren einer Zeichenkette in Speicher ohne ein Byte für ihr abschließendes Nullzeichen kann den Puffer überlaufen lassen.

R

realloc
realloc ist eine C-Bibliotheksfunktion, die die Größe eines zugewiesenen Blocks ändert und seinen Inhalt in einen Block an einer anderen Adresse verschieben kann.
Ein wachsender Puffer kann verschoben werden, wenn unmittelbar hinter seinem aktuellen Block kein Platz verfügbar ist.

S

sbrk
sbrk verändert den Program Break um einen angeforderten Zuwachs in Bytes und gibt bei Erfolg den alten Program Break zurück; ein Zuwachs von null meldet den aktuellen Wert.
Ein erfolgreiches sbrk(1024) bei Program Break 20.000 gibt 20.000 zurück und hinterlässt den neuen Program Break bei 21.024.
Schutzbits
Schutzbits legen erlaubte Operationen auf einem Bereich fest, etwa Lesen, Schreiben oder Ausführen, und werden getrennt von den Grenzen geprüft.
Ein gültiger Code-Offset löst dennoch eine Ausnahme aus, wenn der Prozess versucht, Speicher mit Lese- und Ausführungsberechtigung zu beschreiben.
Segment
Ein Segment ist ein zusammenhängender logischer Bereich eines Adressraums mit unabhängigem Übersetzungszustand, etwa ein Code-, Heap- oder Stackbereich.
Das Heap-Segment kann verschoben werden, ohne dass das Codesegment in denselben physischen Bereich verschoben werden muss.
Segmentierung
Segmentierung übersetzt logische Bereiche unabhängig voneinander anhand segmentspezifischer Zustände und erlaubt so eine physische Platzierung, die vom virtuellen Aufbau abweicht.
Ein virtueller 16-KB-Adressraum kann für seine drei aktuell genutzten Segmente nur 7 KB physischen Speicher benötigen.
Segmentoffset
Ein Segmentoffset bestimmt ein Byte relativ zum virtuellen Ursprung seines Segments; bei der Übersetzung nach oben wird der geprüfte Offset zur physischen Basis addiert.
Das virtuelle Byte 5.100 in einem bei 4.096 beginnenden Heap hat vor der physischen Relokation den Offset 1.004.
Segmentselektor
Ein Segmentselektor bestimmt bei einer expliziten virtuellen Adresse getrennt vom Offsetfeld, welcher Übersetzungszustand eines Segments gilt.
Bei einer 14-Bit-Aufteilung können die führenden Bits 01 den Heap auswählen, während die unteren zwölf Bits sein Byte bestimmen.
Segmenttabelle
Eine Segmenttabelle speichert Übersetzungs- und Schutzinformationen für viele Segmente und unterstützt mehr Bereiche als einige feste Registerpaare.
Bei feingranularer Segmentierung werden Basis, Größe und Zugriffsrechte jedes ausgewählten Bereichs in einer Tabelle nachgeschlagen.
Slab-Allokator
Ein Slab-Allokator hält Caches wiederkehrend benötigter Objekttypen in Slabs und unterstützt schnelle Objektwiederverwendung sowie weniger wiederholte Initialisierungsarbeit.
Ein Kernel kann einen weiteren Prozessdeskriptor aus seinem Objektcache erhalten, statt einen neuen aufzubauen.
Speicherleck
Ein Speicherleck entsteht, wenn zugewiesener Speicher nicht mehr freigegeben wird, obwohl dies nötig wäre, häufig weil das Programm seinen letzten nutzbaren Zeiger darauf verliert.
Eine Funktion, die zurückkehrt, ohne ihren einzigen Heap-Zeiger zu behalten oder den Block freizugeben, hinterlässt einen unerreichbaren Block.
Speicherverwaltungseinheit
Die Speicherverwaltungseinheit ist Prozessorhardware, die virtuelle Adressen übersetzt und Zugriffsregeln anhand des vom Betriebssystem eingerichteten Zustands prüft.
Die MMU übersetzt einen Befehlsabruf, ohne für jeden Befehl eine Betriebssystemroutine aufzurufen.
Speicherzugriffsverletzung
Eine Speicherzugriffsverletzung ist ein gemeldeter unzulässiger Speicherzugriff; die Bezeichnung bleibt auch gebräuchlich, wenn der Rechner keine klassische Segmentierung verwendet.
Eine Berechtigungsverletzung kann eine Speicherzugriffsverletzung auslösen, selbst wenn ihr Offset innerhalb des Bereichs liegt.
Sperrkonkurrenz
Sperrkonkurrenz entsteht, wenn nebenläufige Threads auf den Zugriff auf denselben geschützten Allokatorzustand warten und dadurch der Fortschritt paralleler Speicherzuweisungen begrenzt wird.
Lokale Caches und mehrere Arenen verringern die Anzahl der Speicherzuweisungen, die eine gemeinsame Sperre benötigen.
Stack
Ein Stack ist ein Bereich für den Zustand von Funktionsaufrufen, der im üblichen Ausführungsmodell Rücksprungadressen und gewöhnliche lokale Variablen umfasst.
Die Rückkehr aus einer Funktion gibt ihren Stackframe frei, aber nicht einen von ihr zugewiesenen Heap-Block.
Statische Relokation
Statische Relokation schreibt Adressen beim Laden eines Programms passend zu seiner physischen Platzierung um, ohne selbst den Schutz von Speicherzugriffen durchzusetzen.
Ändert sich der Ladeort, muss das Ladeprogramm eingebettete Adressen für die neue Position umschreiben.
Statischer Speicher
Statischer Speicher ist Speicher für globale und statische lokale Variablen, der unabhängig von einzelnen Aufrufen während der gesamten Programmausführung bestehen bleibt.
Ein statischer lokaler Zähler behält seinen Wert zwischen Aufrufen, statt mit jedem Stackframe zu verschwinden.
Strategie
Eine Strategie ist eine Regel zur Auswahl zwischen möglichen Handlungen, etwa welcher freie physische Bereich einen neu erzeugten Prozess aufnehmen soll.
Die Wahl der kleinsten ausreichenden Lücke ist eine Platzierungsstrategie einer Speicherverwaltung.

T

Transparenz
Transparenz ist die Eigenschaft, die einem Programm erlaubt, seine eigene Speichersicht zu nutzen, ohne die dahinterliegende physische Platzierung oder gemeinsame Nutzung zu verwalten.
Ein Prozess behält dieselben virtuellen Zeiger, nachdem das Betriebssystem seinen physischen Speicher relokiert hat.

V

Verschmelzen freier Blöcke
Das Verschmelzen freier Blöcke verbindet benachbarte freie Blöcke zu einem größeren, ohne aktive Speicherzuweisungen zu verschieben, und verringert so die Zahl getrennter Freispeicherlisteneinträge.
Die Freigabe eines dazwischenliegenden Blocks kann erlauben, zwei benachbarte Lücken zu einem nutzbaren Zuweisungsbereich zu verbinden.
Verwaltungskopf
Ein Verwaltungskopf besteht aus Metadaten vor zurückgegebenen Nutzdaten und erfasst üblicherweise die Größe, damit der Allokator ein späteres free verarbeiten kann.
Bei einem sechzehn Byte großen Verwaltungskopf führt ein Nutzdatenzeiger von 8.016 zu Metadaten bei 8.000 zurück.
Virtuelle Adresse
Eine virtuelle Adresse bezeichnet ein Byte in der Speichersicht eines Prozesses und wird übersetzt, bevor der Prozessor auf den physischen Speicher zugreift.
Ein von einem C-Programm ausgegebener Zeiger zeigt eine virtuelle Adresse, selbst wenn das Betriebssystem den zugehörigen physischen Speicher verschiebt.

W

Wachstumsrichtung
Die Wachstumsrichtung eines Segments hält fest, ob sich sein gültiger Bereich von einem ersten Byte nach oben oder von einer oberen Grenze nach unten erstreckt.
Die Richtung des Stacks verändert, wie die Hardware das untere Adressfeld interpretiert, bevor sie die Basis addiert.
Worst-Fit-Verfahren
Das Worst-Fit-Verfahren wählt den größten freien Block, um einen nutzbaren Rest zu hinterlassen, verbraucht dabei aber möglicherweise eine für spätere große Anforderungen benötigte Lücke.
Eine kleine Anforderung in der einzigen großen Lücke zu platzieren, kann die nächste größere Speicherzuweisung scheitern lassen.

Z

Zusammenhängender Speicherbereich
Ein zusammenhängender Speicherbereich ist ein ununterbrochener Bereich benachbarter Byteadressen, der beim einfachen Verfahren mit Basis- und Grenzregister für einen ganzen Prozess benötigt wird.
Getrennte physische Lücken können zusammen keinen einzelnen Bereich aufnehmen, der zusammenhängend sein muss.

Über diese Einheit

Von StudeliFehler melden

Erstelle deine eigenen Lernmaterialien

Mach aus deinen Notizen, Folien und Dokumenten Lernleitfäden, Übungsfragen, Karteikarten und 6 weitere Tools.

Jetzt starten