x86-Speichersegmentierung - x86 memory segmentation

x86-Speichersegmentierung bezieht sich auf die Implementierung der Speichersegmentierung in der Intel x86- Computer- Befehlssatzarchitektur . Die Segmentierung wurde 1978 auf dem Intel 8086 eingeführt , um es Programmen zu ermöglichen, mehr als 64 KB (65.536  Byte ) Speicher zu adressieren. Der Intel 80286 führte 1982 eine zweite Version der Segmentierung ein, die Unterstützung für virtuellen Speicher und Speicherschutz hinzufügte . An dieser Stelle wurde das ursprüngliche Modell in Realmodus umbenannt und die neue Version wurde als geschützter Modus bezeichnet . Die 2003 eingeführte x86-64- Architektur hat die Unterstützung für die Segmentierung im 64-Bit-Modus weitgehend eingestellt.

Sowohl im realen als auch im geschützten Modus verwendet das System 16-Bit- Segmentregister , um die tatsächliche Speicheradresse abzuleiten. In Realmodus wird die Register CS, DS, SS und ES Punkt auf das aktuellen Programm verwendeten Codesegment (CS), das aktuelle Datensegment (DS), das aktuelle Stapelsegment (SS) und ein zusätzlichen Segment bestimmt durch den Programmierer (ES). Der 1985 eingeführte Intel 80386 fügt zwei zusätzliche Segmentregister hinzu, FS und GS, ohne dass spezifische Verwendungen durch die Hardware definiert sind. Die Verwendung der Segmentregister unterscheidet sich zwischen den beiden Modi.

Die Auswahl des Segments wird normalerweise vom Prozessor entsprechend der ausgeführten Funktion vorgegeben. Anweisungen werden immer aus dem Codesegment geholt. Jeder Stack-Push oder -Pop oder jede Datenreferenz, die sich auf den Stack bezieht, verwendet das Stack-Segment. Alle anderen Verweise auf Daten verwenden das Datensegment. Das zusätzliche Segment ist das Standardziel für String-Operationen (zum Beispiel MOVS oder CMPS). FS und GS haben keine hardwarebedingten Verwendungen. Das Befehlsformat ermöglicht ein optionales Segmentpräfixbyte, das verwendet werden kann, um das Standardsegment für ausgewählte Befehle bei Bedarf zu überschreiben.

Real-Modus

Drei Segmente im Real-Mode- Speicher (zum Vergrößern auf das Bild klicken). Es gibt eine Überlappung zwischen Segment 2 und Segment 3; die Bytes im türkisfarbenen Bereich können von beiden Segmentselektoren verwendet werden.

Im Real-Modus oder V86-Modus kann die Größe eines Segments von 1 Byte bis zu 65.536 Byte reichen (unter Verwendung von 16-Bit-Offsets).

Der 16-Bit-Segmentselektor im Segmentregister wird als die höchstwertigen 16 Bits einer linearen 20-Bit-Adresse interpretiert, die als Segmentadresse bezeichnet wird, von denen die verbleibenden vier niedrigstwertigen Bits alle Nullen sind. Die Segmentadresse wird immer zu einem 16-Bit-Offset im Befehl hinzugefügt, um eine lineare Adresse zu erhalten, die in diesem Modus der physikalischen Adresse entspricht . Zum Beispiel hat die segmentierte Adresse 06EFh:1234h (hier bedeutet das Suffix "h" hexadezimal ) einen Segmentselektor von 06EFh, der eine Segmentadresse von 06EF0h repräsentiert, zu der der Offset addiert wird, was die lineare Adresse 06EF0h + 1234h = 08124h ergibt.

  0000 0110 1110 1111  0000 Segment , 16 Bit, 4 Bit nach links verschoben (oder mit 0x10 multipliziert)
+      0001 0010 0011 0100 Versatz , 16 Bit
                          
  0000 1000 0001 0010 0100 Adresse , 20 Bit

Aufgrund der Art und Weise, wie Segmentadresse und Offset hinzugefügt werden, kann eine einzelne lineare Adresse auf bis zu 2 12 = 4096 eindeutige Segment:Offset-Paare abgebildet werden. Beispielsweise kann die lineare Adresse 08124h die segmentierten Adressen 06EFh:1234h, 0812h:0004h, 0000h:8124h usw. haben.

Dies könnte für Programmierer, die an eindeutige Adressierungsschemata gewöhnt sind, verwirrend sein, aber es kann auch vorteilhaft verwendet werden, beispielsweise wenn mehrere verschachtelte Datenstrukturen adressiert werden. Während Real - Mode - Segmente immer 64  KB lang, ist die praktische Wirkung nur , dass kein Segment mehr als 64 KB sein kann, anstatt dass jedes Segment muss 64 KB lang sein. Da es im Realmodus keinen Schutz oder keine Einschränkung der Rechte gibt, wäre es selbst dann, wenn ein Segment kleiner als 64 KB sein könnte, immer noch ganz den Programmen überlassen, ihre Segmente zu koordinieren und innerhalb der Grenzen ihrer Segmente zu halten, wie es jedes Programm kann immer auf jeden Speicher zugreifen (da er willkürlich Segmentselektoren setzen kann, um Segmentadressen ohne jegliche Überwachung zu ändern). Daher kann man sich den Real-Mode genauso gut vorstellen, dass er für jedes Segment eine variable Länge im Bereich von 1 bis 65.536 Byte hat, die von der CPU einfach nicht erzwungen wird.

(Die führenden Nullen der linearen Adresse, der segmentierten Adressen sowie der Segment- und Offset-Felder sind hier der Übersichtlichkeit halber dargestellt. Sie werden normalerweise weggelassen.)

Der effektive 20-Bit- Adressraum des Realmodus begrenzt den adressierbaren Speicher auf 2 20  Byte oder 1.048.576 Byte (1  MB ). Dies leitete sich direkt vom Hardware-Design des Intel 8086 (und später des eng verwandten 8088) ab, der genau 20 Adresspins hatte . (Beide waren in 40-Pin-DIP-Gehäusen verpackt; selbst bei nur 20 Adressleitungen wurden die Adress- und Datenbusse gemultiplext, um alle Adress- und Datenleitungen innerhalb der begrenzten Pinzahl zu passen.)

Jedes Segment beginnt bei einem Vielfachen von 16 Byte, einem sogenannten Absatz , vom Anfang des linearen (flachen) Adressraums. Das heißt, in 16-Byte-Intervallen. Da alle Segmente 64 KB lang sind, erklärt dies, wie Überlappungen zwischen den Segmenten auftreten können und warum auf jede Stelle im linearen Speicheradressraum mit vielen Segment:Offset-Paaren zugegriffen werden kann. Die tatsächliche Lage des Anfangs eines Segments im linearen Adressraum kann mit Segment × 16 berechnet werden. Ein Segmentwert von 0Ch (12) würde eine lineare Adresse bei C0h (192) im linearen Adressraum ergeben. Zu dieser Zahl kann dann der Adressoffset addiert werden. 0Ch:0Fh (12:15) wäre C0h+0Fh=CFh (192+15=207), wobei CFh (207) die lineare Adresse ist. Solche Adressübersetzungen werden von der Segmentierungseinheit der CPU ausgeführt. Das letzte Segment, FFFFh (65535), beginnt bei linearer Adresse FFFF0h (1048560), 16 Byte vor dem Ende des 20-Bit-Adressraums und kann somit mit einem Offset von bis zu 65.536 Byte auf bis zu 65.520 (65536 .) zugreifen −16) Bytes nach dem Ende des 20-Bit-8088-Adressraums. Auf dem 8088 wurden diese Adresszugriffe bis zum Anfang des Adressraums umgebrochen, sodass 65535:16 auf die Adresse 0 zugreifen würde und 65533:1000 auf die Adresse 952 des linearen Adressraums zugreifen würde. Die Verwendung dieser Funktion durch Programmierer führte zu Kompatibilitätsproblemen mit Gate A20 in späteren CPU-Generationen, bei denen der lineare Adressraum auf über 20 Bit erweitert wurde.

Im 16-Bit-Realmodus ist es ziemlich komplex, Anwendungen die Nutzung mehrerer Speichersegmente zu ermöglichen (um auf mehr Speicher zuzugreifen, als in einem 64K-Segment verfügbar ist), wurde jedoch für alle außer den kleinsten Tools als notwendiges Übel angesehen ( was mit weniger Speicher auskommen könnte). Die Wurzel des Problems liegt darin, dass keine geeigneten adressarithmetischen Anweisungen zur Verfügung stehen, die für eine flache Adressierung des gesamten Speicherbereichs geeignet sind. Eine flache Adressierung ist durch die Anwendung mehrerer Befehle möglich, was jedoch zu langsameren Programmen führt.

Das Speichermodellkonzept leitet sich vom Aufbau der Segmentregister ab. Im winzigen Modell CS=DS=SS zum Beispiel sind der Code, die Daten und der Stack des Programms alle in einem einzigen 64-KB-Segment enthalten. Im Small- Memory-Modell DS=SS befinden sich also sowohl Daten als auch Stack im selben Segment; CS zeigt auf ein anderes Codesegment von bis zu 64 KB.

Sicherheitsmodus

Drei Segmente im geschützten Modusspeicher (zum Vergrößern auf das Bild klicken), mit der lokalen Deskriptortabelle .

80286 geschützter Modus

Der 80286 ‚s geschützte Modus erweitert den Adressraum des Prozessors 2 zu 24 Byte (16 Megabyte), jedoch nicht durch den Verschiebungswert eingestellt wird . Stattdessen enthalten die 16-Bit-Segmentregister jetzt einen Index in eine Tabelle von Segmentdeskriptoren, die 24-Bit-Basisadressen enthalten, zu denen der Offset hinzugefügt wird. Um alte Software zu unterstützen, startet der Prozessor im "Real Mode", einem Modus, in dem er das segmentierte Adressierungsmodell des 8086 verwendet. Es gibt jedoch einen kleinen Unterschied: Die resultierende physikalische Adresse wird nicht mehr auf 20 Bit gekürzt, also real mode- Zeiger (aber nicht 8086-Zeiger) können jetzt auf Adressen zwischen 100000 16 und 10FFEF 16 verweisen . Dieser etwa 64-Kilobyte große Speicherbereich war als High Memory Area (HMA) bekannt, und spätere Versionen von DOS konnten ihn verwenden, um den verfügbaren "konventionellen" Speicher (dh innerhalb des ersten MB ) zu vergrößern . Mit dem Hinzufügen des HMA beträgt der gesamte Adressraum ungefähr 1,06 MB. Obwohl der 80286 Real-Mode-Adressen nicht auf 20 Bit kürzt, kann ein System, das einen 80286 enthält, dies mit Hardware außerhalb des Prozessors tun, indem es die 21. Adressleitung, die A20-Leitung, abschaltet . Die Hardware dafür lieferte der IBM PC AT (für volle Abwärtskompatibilität mit der Software für die ursprünglichen IBM PC- und PC/XT- Modelle) und so auch alle nachfolgenden PC-Klone der " AT- Klasse".

Der 286-geschützte Modus wurde selten verwendet, da er die große Anzahl von Benutzern mit 8086/88-Computern ausgeschlossen hätte. Darüber hinaus war es immer noch erforderlich, den Speicher in 64k-Segmente aufzuteilen, wie es im Real-Modus gemacht wurde. Diese Einschränkung kann bei 32-Bit-CPUs umgangen werden, die die Verwendung von Speicherzeigern mit einer Größe von mehr als 64 KB zulassen. Da das Feld Segmentlimit jedoch nur 24 Bit lang ist, kann die maximale Segmentgröße, die erstellt werden kann kann verwendet werden, um mehr Speicher zuzuweisen, kein einzelnes Segment darf 16 MB überschreiten). Diese Methode wurde häufig in Windows 3.x-Anwendungen verwendet, um einen flachen Speicherplatz zu erzeugen, obwohl das Betriebssystem selbst noch 16-Bit war, konnten API-Aufrufe nicht mit 32-Bit-Anweisungen ausgeführt werden. Daher war es immer noch erforderlich, den gesamten Code, der API-Aufrufe durchführt, in 64k-Segmente zu platzieren.

Nachdem der 286-geschützte Modus aufgerufen wurde, konnte er nur durch Ausführen eines Hardware-Resets verlassen werden. Maschinen, die dem steigenden IBM PC/AT- Standard folgten, konnten über den standardisierten Tastatur-Controller der CPU einen Reset vortäuschen, was jedoch deutlich träge war. Windows 3.x hat diese beiden Probleme umgangen, indem es absichtlich einen dreifachen Fehler in den Interrupt-Handling-Mechanismen der CPU auslöste , der dazu führte, dass die CPU fast sofort in den Realmodus zurückfiel.

Detaillierter Workflow für Segmentierungseinheiten

Eine logische Adresse besteht aus einem 16-Bit-Segmentselektor (der 13+1 Adressbits liefert) und einem 16-Bit-Offset. Der Segmentselektor muss sich in einem der Segmentregister befinden. Dieser Selektor besteht aus einem 2-Bit Requested Privilege Level (RPL), einem 1-Bit Table Indicator (TI) und einem 13-Bit Index.

Beim Versuch der Adressübersetzung einer gegebenen logischen Adresse liest der Prozessor die 64-Bit- Segmentdeskriptorstruktur entweder aus der globalen Deskriptortabelle, wenn TI = 0, oder der lokalen Deskriptortabelle, wenn TI = 1 ist. Es führt dann die Berechtigungsprüfung durch:

max(CPL, RPL) ≤ DPL

wobei CPL die aktuelle Privilegstufe ist (in den unteren 2 Bits des CS-Registers gefunden), RPL die vom Segmentselektor angeforderte Privilegstufe ist und DPL die Deskriptorprivilegstufe des Segments (in dem Deskriptor gefunden) ist. Alle Berechtigungsstufen sind ganze Zahlen im Bereich 0–3, wobei die niedrigste Zahl der höchsten Berechtigung entspricht.

Wenn die Ungleichung falsch ist, erzeugt der Prozessor einen allgemeinen Schutzfehler (GP) . Andernfalls wird die Adressübersetzung fortgesetzt. Der Prozessor nimmt dann den 32-Bit- oder 16-Bit-Offset und vergleicht ihn mit der im Segmentdeskriptor angegebenen Segmentgrenze. Wenn es größer ist, wird ein GP-Fehler generiert. Andernfalls fügt der Prozessor die im Deskriptor angegebene 24-Bit-Segmentbasis zum Offset hinzu, wodurch eine lineare physikalische Adresse erzeugt wird.

Die Berechtigungsprüfung wird nur durchgeführt, wenn das Segmentregister geladen wird, da Segmentdeskriptoren in versteckten Teilen der Segmentregister zwischengespeichert werden.

80386 geschützter Modus

Beim Intel 80386 und höher behält der geschützte Modus den Segmentierungsmechanismus des geschützten 80286-Modus bei, aber eine Paging- Einheit wurde als zweite Schicht der Adressübersetzung zwischen der Segmentierungseinheit und dem physikalischen Bus hinzugefügt. Wichtig ist auch, dass Adressoffsets 32 Bit (anstelle von 16 Bit) betragen und die Segmentbasis in jedem Segmentdeskriptor ebenfalls 32 Bit (anstelle von 24 Bit) beträgt. Die allgemeine Funktionsweise der Segmentierungseinheit bleibt ansonsten unverändert. Die Paging-Einheit kann aktiviert oder deaktiviert sein; wenn deaktiviert, ist der Betrieb der gleiche wie beim 80286. Wenn die Paging-Einheit aktiviert ist, sind die Adressen in einem Segment jetzt virtuelle Adressen und nicht wie beim 80286 physikalische Adressen. Das heißt, die Segment-Startadresse, der Offset, und die letzte 32-Bit-Adresse der Segmentierungseinheit, die durch Addieren der beiden abgeleitet wird, sind alle virtuelle (oder logische) Adressen, wenn die Paging-Einheit aktiviert ist. Wenn die Segmentierungseinheit diese virtuellen 32-Bit-Adressen erzeugt und validiert, übersetzt die aktivierte Paging-Einheit schließlich diese virtuellen Adressen in physikalische Adressen. Die physikalischen Adressen sind beim 386 32-Bit , können aber bei neueren Prozessoren, die Physical Address Extension unterstützen, größer sein .

Der 80386 führte auch zwei neue Allzweck-Datensegmentregister, FS und GS, zu dem ursprünglichen Satz von vier Segmentregistern (CS, DS, ES und SS) ein.

Eine 386er CPU kann durch Löschen eines Bits im CR0-Steuerregister wieder in den Realmodus versetzt werden, dies ist jedoch eine privilegierte Operation, um Sicherheit und Robustheit zu erzwingen. Zum Vergleich: Ein 286 konnte nur durch Erzwingen eines Prozessor-Resets, zB durch einen Dreifachfehler oder mit externer Hardware , in den Real-Modus zurückversetzt werden.

Spätere Entwicklungen

Die x86-64- Architektur verwendet keine Segmentierung im Long-Modus (64-Bit-Modus). Vier der Segmentregister CS, SS, DS und ES werden auf 0 und die Grenze auf 2 64 gezwungen . Die Segmentregister FS und GS können immer noch eine von Null verschiedene Basisadresse haben. Dadurch können Betriebssysteme diese Segmente für spezielle Zwecke verwenden. Im Gegensatz zum globalen Deskriptortabellenmechanismus , der von Legacy-Modi verwendet wird, wird die Basisadresse dieser Segmente in einem modellspezifischen Register gespeichert . Die x86-64-Architektur stellt außerdem den speziellen SWAPGS- Befehl bereit , der das Vertauschen der Kernelmodus- und Benutzermodus- Basisadressen ermöglicht.

Beispielsweise verwendet Microsoft Windows auf x86-64 das GS-Segment, um auf den Thread Environment Block zu verweisen , eine kleine Datenstruktur für jeden Thread , die Informationen über die Ausnahmebehandlung, Thread-lokale Variablen und andere Pro-Thread-Status enthält. Ebenso verwendet der Linux-Kernel das GS-Segment, um Daten pro CPU zu speichern.

GS / FS werden auch in verwendet gcc ‚s gewinde lokalen Speicher und Kanarienvogel-basierte Stapelschutz.

Praktiken Methoden Ausübungen

Logische Adressen können in der x86-Assemblersprache explizit angegeben werden , zB (AT&T-Syntax):

movl $42, %fs:(%eax)  ; Equivalent to M[fs:eax]<-42) in RTL

oder in Intel-Syntax :

mov dword [fs:eax], 42

Segmentregister werden jedoch normalerweise implizit verwendet.

  • Alle CPU-Befehle werden implizit aus dem Codesegment geholt, das durch den im CS-Register gehaltenen Segmentselektor spezifiziert wird.
  • Die meisten Speicherreferenzen stammen aus dem Datensegment, das von dem im DS-Register gehaltenen Segmentselektor spezifiziert wird. Diese können auch aus dem zusätzlichen Segment stammen, das von dem im ES-Register gehaltenen Segmentselektor spezifiziert wird, wenn ein Segment-Override-Präfix dem Befehl vorangeht, der den Speicherbezug herstellt. Die meisten, aber nicht alle Anweisungen, die standardmäßig DS verwenden, akzeptieren ein ES-Überschreibungspräfix.
  • Prozessor- Stack- Referenzen, entweder implizit (zB Push- und Pop- Befehle) oder explizit ( Speicherzugriffe unter Verwendung der (E)SP- oder (E)BP-Register ) verwenden das Stack-Segment, das durch den im SS-Register gehaltenen Segmentselektor spezifiziert wird.
  • String-Anweisungen (zB stos , movs ) verwenden zusammen mit dem Datensegment auch das zusätzliche Segment, das durch den im ES-Register gehaltenen Segmentselektor angegeben wird.

Die Segmentierung kann auf x86-32-Prozessoren nicht ausgeschaltet werden (dies gilt auch für den 64-Bit-Modus, aber außerhalb des Rahmens der Diskussion), daher simulieren viele 32-Bit-Betriebssysteme ein flaches Speichermodell, indem sie die Basen aller Segmente auf 0 setzen um die Segmentierung für Programme neutral zu machen. Zum Beispiel richtet der Linux-Kernel nur 4 Allzwecksegmente ein:

Name Beschreibung Base Grenze DPL
__KERNEL_CS Kernel-Code-Segment 0 4 GiB 0
__KERNEL_DS Kernel-Datensegment 0 4 GiB 0
__USER_CS Nutzercode-Segment 0 4 GiB 3
__USER_DS Nutzerdatensegment 0 4 GiB 3

Da die Basis in allen Fällen auf 0 und die Grenze auf 4 GiB gesetzt ist, beeinflusst die Segmentierungseinheit die Adressen nicht, die das Programm ausgibt, bevor sie bei der Paging- Einheit ankommen . (Dies bezieht sich natürlich auf 80386 und spätere Prozessoren, da die früheren x86-Prozessoren keine Paging-Einheit haben.)

Aktuelles Linux verwendet auch GS, um auf Thread-lokalen Speicher zu verweisen .

Segmente können entweder als Code-, Daten- oder Systemsegmente definiert werden. Zusätzliche Berechtigungsbits sind vorhanden, um Segmente schreibgeschützt, lesen/schreiben, ausführen usw.

Im geschützten Modus kann der Code immer alle Segmentregister außer CS (dem Codesegmentselektor ) modifizieren . Dies liegt daran, dass die aktuelle Privilegstufe (CPL) des Prozessors in den unteren 2 Bits des CS-Registers gespeichert wird. Die einzigen Möglichkeiten, die Prozessorberechtigungsebene zu erhöhen (und CS neu zu laden), sind die Befehle lcall ( Far Call) und int (Interrupt) . In ähnlicher Weise sind die einzigen Möglichkeiten zum Herabsetzen der Berechtigungsebene (und zum erneuten Laden von CS) die Befehle lret ( Far Return) und iret (Interrupt Return). Im Real-Modus kann Code auch das CS-Register modifizieren, indem er einen Weitsprung macht (oder einen undokumentierten POP CSBefehl auf dem 8086 oder 8088 verwendet). Im Real-Modus gibt es natürlich keine Berechtigungsstufen; alle Programme haben absolut ungeprüften Zugriff auf den gesamten Speicher und alle CPU-Befehle.

Weitere Informationen zur Segmentierung finden Sie in den IA-32- Handbüchern, die auf den Websites von AMD oder Intel frei verfügbar sind .

Hinweise und Referenzen

Siehe auch

Externe Links