it-swarm.com.de

Verwendet Linux keine Segmentierung, sondern nur Paging?

Die Linux-Programmierschnittstelle zeigt das Layout eines virtuellen Adressraums eines Prozesses. Ist jede Region im Diagramm ein Segment?

(enter image description here

Von Den Linux-Kernel verstehen,

ist es richtig, dass das Folgende bedeutet, dass die Segmentierungseinheit in MMU die Segmente und Offsets innerhalb von Segmenten auf die Adresse des virtuellen Speichers abbildet und die Paging-Einheit dann die Adresse des virtuellen Speichers auf die Adresse des physischen Speichers abbildet ?

Die Speicherverwaltungseinheit (MMU) wandelt eine logische Adresse mittels einer als Segmentierungseinheit bezeichneten Hardwareschaltung in eine lineare Adresse um; Anschließend wandelt eine zweite Hardwareschaltung, die als Paging-Einheit bezeichnet wird, die lineare Adresse in eine physikalische Adresse um (siehe Abbildung 2-1).

(enter image description here

Warum heißt es dann, dass Linux keine Segmentierung verwendet, sondern nur Paging?

Die 80x86-Mikroprozessoren wurden segmentiert, um Programmierer zu ermutigen, ihre Anwendungen in logisch verwandte Entitäten wie Unterprogramme oder globale und lokale Datenbereiche aufzuteilen. Linux verwendet die Segmentierung jedoch nur sehr eingeschränkt. Tatsächlich sind Segmentierung und Paging etwas redundant, da beide zum Trennen der physischen Adressräume verwendet werden können Anzahl der Prozesse: Durch Segmentierung kann jedem Prozess ein anderer linearer Adressraum zugewiesen werden, während durch Paging derselbe lineare Adressraum verschiedenen physischen Adressräumen zugeordnet werden kann. Linux zieht Paging aus folgenden Gründen der Segmentierung vor:

• Die Speicherverwaltung ist einfacher, wenn alle Prozesse dieselben Segmentregisterwerte verwenden, dh wenn sie denselben Satz linearer Adressen verwenden.

• Eines der Entwurfsziele von Linux ist die Portabilität auf eine Vielzahl von Architekturen. Insbesondere RISC-Architekturen unterstützen die Segmentierung nur begrenzt.

Die Linux-Version 2.6 verwendet die Segmentierung nur, wenn dies für die 80x86-Architektur erforderlich ist.

27
Tim

Die x86-64-Architektur verwendet keine Segmentierung im Langmodus (64-Bit-Modus).

Vier der Segmentregister: CS, SS, DS und ES werden auf 0 und die Grenze auf 2 ^ 64 gezwungen.

https://en.wikipedia.org/wiki/X86_memory_segmentation#Later_developments

Das Betriebssystem kann nicht mehr einschränken, welche Bereiche der "linearen Adressen" verfügbar sind. Daher kann die Segmentierung nicht für den Speicherschutz verwendet werden. es muss sich ausschließlich auf Paging verlassen.

Machen Sie sich keine Sorgen über die Details von x86-CPUs, die nur im alten 32-Bit-Modus gelten würden. Linux für die 32-Bit-Modi wird nicht so häufig verwendet. Es kann sogar als "in einem Zustand gütiger Vernachlässigung für mehrere Jahre" betrachtet werden. Siehe 2-Bit x86-Unterstützung in Fedora [LWN.net, 2017].

(Es kommt vor, dass 32-Bit-Linux auch keine Segmentierung verwendet. Aber Sie müssen mir nicht vertrauen, Sie können es einfach ignorieren :-).

21
sourcejedi

Ist jede Region im Diagramm ein Segment?

Nein.

Während das Segmentierungssystem (im 32-Bit-geschützten Modus auf einem x86) separate Code-, Daten- und Stapelsegmente unterstützt, sind in der Praxis alle Segmente auf denselben Speicherbereich eingestellt. Das heißt, sie beginnen bei 0 und enden am Ende des Speichers(*). Dadurch sind die logischen und linearen Adressen gleich.

Dies wird als "flaches" Speichermodell bezeichnet und ist etwas einfacher als das Modell, in dem Sie unterschiedliche Segmente und dann Zeiger darin haben. Insbesondere erfordert ein segmentiertes Modell längere Zeiger, da der Segmentwähler zusätzlich zum Versatzzeiger enthalten sein muss. (16-Bit-Segmentauswahl + 32-Bit-Offset für insgesamt 48-Bit-Zeiger; gegenüber nur einem 32-Bit-Flachzeiger.)

Der 64-Bit-Modus unterstützt nicht einmal eine andere Segmentierung als das Flat-Memory-Modell.

Wenn Sie auf dem 286 im 16-Bit-geschützten Modus programmieren würden, würden Sie mehr Segmente benötigen, da der Adressraum 24 Bit beträgt, die Zeiger jedoch nur 16 Bit.

(* Beachten Sie, dass ich mich nicht erinnern kann, wie 32-Bit-Linux mit der Trennung von Kernel und Benutzerbereich umgeht. Die Segmentierung würde dies ermöglichen, indem die Grenzwerte für Benutzerbereichssegmente so festgelegt werden, dass sie den Kernelspeicher nicht enthalten. Paging ermöglicht dies, da es a bereitstellt Schutzstufe pro Seite.)

Warum heißt es dann, dass Linux keine Segmentierung verwendet, sondern nur Paging?

Der x86 hat immer noch die Segmente und Sie können sie nicht deaktivieren. Sie werden nur so wenig wie möglich verwendet. Im 32-Bit-geschützten Modus müssen die Segmente für das flache Modell eingerichtet werden, und selbst im 64-Bit-Modus sind sie noch vorhanden.

9
ilkkachu

Da der x86 Segmente hat, ist es nicht möglich, diese nicht zu verwenden. Die Basisadressen cs (Codesegment) und ds (Datensegment) werden jedoch auf Null gesetzt, sodass die Segmentierung nicht wirklich verwendet wird. Eine Ausnahme bilden lokale Thread-Daten. Eines der normalerweise nicht verwendeten Segmentregister zeigt auf lokale Thread-Daten. Dies dient jedoch hauptsächlich dazu, zu vermeiden, dass eines der Allzweckregister für diese Aufgabe reserviert wird.

Es heißt nicht, dass Linux auf x86 keine Segmentierung verwendet, da dies nicht möglich wäre. Sie haben bereits einen Teil hervorgehoben: Linux verwendet die Segmentierung nur sehr eingeschränkt. Der zweite Teil ist Linux verwendet die Segmentierung nur, wenn dies für die 80x86-Architektur erforderlich ist

Sie haben bereits die Gründe genannt, Paging ist einfacher und portabler.

8
RalfFriedl

Ist jede Region im Diagramm ein Segment?

Dies sind zwei fast völlig unterschiedliche Verwendungen des Wortes "Segment"

  • x86-Segmentierung/Segmentregister: Moderne x86-Betriebssysteme verwenden ein Flat-Memory-Modell, bei dem alle Segmente im 32-Bit-Modus dieselbe Basis = 0 und dasselbe Limit = max haben, genau wie die Hardware dies in 64 erzwingt -bit-Modus , wodurch die Segmentierung zu einer Art Überbleibsel wird. (Mit Ausnahme von FS oder GS, das auch im 64-Bit-Modus für die threadlokale Speicherung verwendet wird.)
  • Linker/Program-Loader-Abschnitte/Segmente. ( Was ist der Unterschied zwischen Abschnitt und Segment im ELF-Dateiformat )

Die Verwendungen haben einen gemeinsamen Ursprung: Wenn Sie waren ein segmentiertes Speichermodell verwenden (insbesondere ohne ausgelagerten virtuellen Speicher), haben Sie möglicherweise Daten und BSS-Adressen, die relativ zur Segmentbasis DS sind , Stapel relativ zur SS-Basis und Code relativ zur CS-Basisadresse.

So können mehrere verschiedene Programme auf verschiedene lineare Adressen geladen oder sogar nach dem Start verschoben werden, ohne die 16- oder 32-Bit-Offsets relativ zu den Segmentbasen zu ändern.

Aber dann müssen Sie wissen, zu welchem ​​Segment ein Zeiger relativ ist, also haben Sie "Fernzeiger" und so weiter. (Tatsächliche 16-Bit-x86-Programme mussten häufig nicht auf ihren Code als Daten zugreifen, sodass sie irgendwo ein 64k-Codesegment und möglicherweise einen weiteren 64k-Block mit DS = SS verwenden konnten, wobei der Stapel aus hohen Offsets und Daten bei wuchs oder ein winziges Codemodell mit allen Segmentbasen gleich).


Wie die x86-Segmentierung mit dem Paging interagiert

Die Adresszuordnung im 32/64-Bit-Modus lautet:

  1. segment: Offset (Segmentbasis, die durch das Register impliziert wird, das den Offset enthält, oder mit einem Befehlspräfix überschrieben wird)
  2. Lineare virtuelle 32- oder 64-Bit-Adresse = Basis + Offset. (In einem Flat-Memory-Modell wie Linux sind Zeiger/Offsets auch lineare Adressen. Außer beim Zugriff auf TLS relativ zu FS oder GS.)
  3. seitentabellen (vom TLB zwischengespeichert) werden linear auf die physische Adresse 32 (Legacy-Modus), 36 (Legacy-PAE) oder 52-Bit (x86-64) abgebildet. ( https://stackoverflow.com/questions/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the ) .

    Dieser Schritt ist optional: Paging muss während des Startvorgangs durch Setzen eines Bits in einem Steuerregister aktiviert werden. Ohne Paging sind lineare Adressen physikalische Adressen.

Beachten Sie, dass durch die Segmentierung nicht Sie mehr als 32 oder 64 Bit virtuellen Adressraum in einem einzelnen Prozess (oder Thread) verwenden können , weil der flache (lineare) Adressraum, in den alles abgebildet ist, nur die gleiche Anzahl von Bits hat wie die Offsets selbst. (Dies war bei 16-Bit-x86 nicht der Fall, wo die Segmentierung tatsächlich nützlich war, um mehr als 64 KB Speicher mit hauptsächlich 16-Bit-Registern und Offsets zu verwenden.)


Die CPU speichert Segmentdeskriptoren zwischen, die vom GDT (oder LDT) geladen wurden, einschließlich der Segmentbasis. Wenn Sie einen Zeiger dereferenzieren, wird standardmäßig entweder DS oder SS als Segment verwendet, je nachdem, in welchem ​​Register er sich befindet. Der Registerwert (Zeiger) wird als Versatz von der Segmentbasis behandelt.

Da die Segmentbasis normalerweise Null ist, tun CPUs dies im Sonderfall. Oder aus einer anderen Perspektive, wenn Sie do Eine Segmentbasis ungleich Null haben, haben Lasten eine zusätzliche Latenz, da der "spezielle" (normale) Fall der Umgehung des Hinzufügens der Basisadresse nicht zutrifft.


So richtet Linux x86-Segmentregister ein:

Die Basis und das Limit von CS/DS/ES/SS sind alle 0/-1 im 32- und 64-Bit-Modus. Dies wird als Flat-Memory-Modell bezeichnet, da alle Zeiger auf denselben Adressraum zeigen.

(AMD-CPU-Architekten kastrierten die Segmentierung durch erzwingen ein Flat-Memory-Modell für den 64-Bit-Modus, da die Mainstream-Betriebssysteme es ohnehin nicht verwendeten, mit Ausnahme des No-Exec-Schutzes, der viel besser bereitgestellt wurde übrigens durch Paging mit dem PAE- oder x86-64-Seitentabellenformat.)

  • TLS (Thread Local Storage): FS und GS sind nicht im langen Modus auf base = 0 festgelegt. (Sie waren neu mit 386 und wurden von keiner Anweisung implizit verwendet, nicht einmal von den rep- Zeichenfolgenanweisungen, die ES verwenden). x86-64 Linux setzt die FS Basisadresse für jeden Thread auf die Adresse des TLS-Blocks.

    z.B. mov eax, [fs: 16] Lädt einen 32-Bit-Wert aus 16 Bytes in den TLS-Block für diesen Thread.

  • der CS-Segmentdeskriptor wählt aus, in welchem ​​Modus sich die CPU befindet (16/32/64-Bit-geschützter Modus/langer Modus). Linux verwendet einen einzelnen GDT-Eintrag für alle 64-Bit-User-Space-Prozesse und einen weiteren GDT-Eintrag für alle 32-Bit-User-Space-Prozesse. (Damit die CPU richtig funktioniert, muss DS/ES ebenso wie SS auf gültige Einträge gesetzt werden.) Außerdem wird die Berechtigungsstufe (Kernel (Ring 0) vs. Benutzer (Ring 3)) ausgewählt. Selbst wenn Sie zum 64-Bit-Benutzerbereich zurückkehren, muss der Kernel mithilfe von iret eine Änderung der CS veranlassen oder sysret anstelle einer normalen Sprung- oder Ret-Anweisung.

  • In x86-64 verwendet der Einstiegspunkt syscallswapgs, um die GS von der GS des Benutzerraums zum Kernel zu wechseln, um den Kernelstapel für diesen Thread zu finden. (Ein spezieller Fall von threadlokaler Speicherung). Die Anweisung syscall ändert den Stapelzeiger nicht so, dass er auf den Kernelstapel zeigt. Es zeigt immer noch auf den Benutzerstapel, wenn der Kernel den Einstiegspunkt erreicht1.

  • DS/ES/SS muss auch auf gültige Segmentdeskriptoren eingestellt sein, damit die CPU im geschützten Modus/Langmodus arbeitet, obwohl die Basis/Grenze dieser Deskriptoren im Langmodus ignoriert wird.

Grundsätzlich wird die x86-Segmentierung für TLS und für die obligatorischen x86-Osdev-Inhalte verwendet, die von der Hardware ausgeführt werden müssen.


Fußnote 1: Unterhaltsame Geschichte: Es gibt Mailinglisten-Archive mit Nachrichten zwischen Kernel-Entwicklern und AMD-Architekten, die einige Jahre vor der Veröffentlichung von AMD64-Silizium veröffentlicht wurden, was zu Optimierungen des Designs von syscall führte, sodass es verwendet werden konnte. Siehe Links in diese Antwort für Details.

3
Peter Cordes

Linux x86/32 verwendet keine Segmentierung in dem Sinne, dass alle Segmente mit derselben linearen Adresse und Grenze initialisiert werden. Für die x86-Architektur muss das Programm Segmente haben: Code kann nur aus dem Codesegment ausgeführt werden, der Stapel kann sich nur im Stapelsegment befinden, Daten können nur in einem der Datensegmente bearbeitet werden. Linux umgeht diesen Mechanismus, indem alle Segmente auf dieselbe Weise festgelegt werden (mit Ausnahmen, die in Ihrem Buch ohnehin nicht erwähnt werden), sodass in jedem Segment dieselbe logische Adresse gültig ist. Dies ist in der Tat gleichbedeutend damit, überhaupt keine Segmente zu haben.

3