Ist S=1, handelt es sich also um ein Applikationssegment, so hat das Feld Typ im Segmentdeskriptor folgenden Aufbau:
In der umseitigen Tabelle finden sich alle möglichen Kombinationen der Bits EXE, E/C, W/R und A sowie deren Bedeutung.
Legt man den Begriff Daten möglichst weit aus, so besagt er soviel wie Information. In diesem Sinne sind auch Programme Daten, da sie (logischerweise) eine Menge von Information (eine Folge von Bits) darstellen (nämlich was der Computer machen soll). Daß uns diese Art der Information nicht immer unmittelbar einsichtig erscheint, ist dabei ohne Relevanz.
Im engeren Sinne trifft man aber doch eine Unterscheidung zwischen Daten und Programmen: Daten sind Information, die be- oder verarbeitet werden soll, Programme stellen die Werkzeuge dar, mit denen die Daten be- und verarbeitet werden. Wesentlich unterscheiden sich diese beiden Kategorien vor allem darin, daß die Datenmenge "Programm" während ihrer Ausführung im allgemeinen nicht verändert wird, Daten im engeren Sinne aber gerade bearbeitet und dadurch verändert werden. Diese Differenzierung des allgemeinen Begriffes "Daten" spiegelt sich auch bei den Segmentdeskriptoren wider: Es gibt ausführbare, d.h. Programmsegmente, sowie nicht ausführbare, d.h. Datensegmente. Markiert wird diese Unterscheidung durch das Bit EXE (Execute): ist EXE gleich 1, so handelt es sich um ein Programmsegment, ansonsten um ein Datensegment.
Datensegmente sind implizit stets als lesbar gekennzeichnet. Sie können jedoch erst dann beschrieben werden, wenn das Bit W (Write) gesetzt ist. Ein Schreibversuch auf ein Segment mit gelöschtem Write-Bit (W=0) löst die Exception "allgemeiner Protection-Fehler" und damit den Interrupt 0dh aus. Datensegmente können sich darin unterscheiden, in welche Richtung sie jeweils "wachsen". Die Richtung wird dabei durch das Bit E (Expand-down) definiert. Es verändert die Bedeutung des Limits: Bei einem sich nach oben erstreckenden Segment (E=0) muß der Offset eines Objekts stets kleiner oder darf höchstens gleich dem Limit sein. Demgegenüber muß bei einem sich nach unten erstreckenden Segment (E=1) der Offset immer einen größeren Wert als das Limit aufweisen. Bei gesetztem E-Bit wächst das Segment also nach unten, ansonsten nach oben. Ein Beispiel für ein nach unten wachsendes Segment wäre das Stack-Segment, da der Stack bei einer Vergrößerung durch den dekrementierten Stack-Zeiger ESP nach unten wächst.
Greift der Prozessor auf Daten oder Code in einem Segment zu, so setzt er automatisch das Bit A (Accessed) im entsprechenden Deskriptor. Das Betriebssystem ist dadurch in der Lage festzustellen, welche Segmente häufig angesprochen werden. Das ist wichtig, wenn ein Segment auf Festplatte ausgelagert werden soll, um Platz für ein neues zu schaffen.
Programmsegmente sind im Gegensatz zu Datensegmenten implizit stets als nicht-beschreibbar gekennzeichnet, der Prozessor kann daher niemals einen Schreibzugriff auf ein solches Codesegment ausführen. Diese Vorkehrung dient zum Schutz gegen Programmfehler und ist Bestandteil der Schutzphilosophie des Protected Mode. Unter MS-DOS führen Bugs häufig zum Überschreiben von Programmcode - der PC stürzt ab. Das wird durch den impliziten Schreibschutz von Codesegmenten verhindert. Manchmal ist es aber dennoch notwendig, Code oder Immediate-Operanden (die ja Bestandteil des Befehlsstromes und dadurch des Codesegments sind) während der Laufzeit zu überschreiben. Zu diesem Zweck sieht die Implementierung des Protected Mode vor, ein Code- und ein Datensegment zu überlagern, beispielsweise indem ein Segment als Codesegment gekennzeichnet wird, während man ein zweites Segment mit gleicher Basisadresse und gleichem Limit als Datensegment definiert. In diesem Fall kann der i386 über den Umweg eines sogenannten Alias mit Hilfe des Datensegments Code überschreiben. Das ist natürlich nicht ganz ungefährlich, weil nun im Prinzip die gleichen, oben angeführten, Fehler auftreten können.
Ist das Bit R (Read) im Segmentdeskriptor eines Codesegments gesetzt (R=1), kann das Segment nicht nur ausgeführt, sondern auch über einen MOV-Befehl direkt in ein Vielzweckregister übertragen werden. Das ist beispielsweise notwendig, wenn im Programmcode Daten eingebettet sind (also Immediate-Operanden oder Tabellen). Im ROM-BIOS finden sich häufig Tabellen für Festplattentypen, Basisadressen des Bildschirmspeichers etc. Wenn die Tabellen nur gelesen werden sollen, kann man sich über das R-Bit ein aufwendiges und fehleranfälliges Alias sparen.
Um auch ein möglichst effektives Auslagern von Codesegmenten auf einen Massenspeicher zu ermöglichen, weist der Segmentdeskriptor für ein Codesegment ebenfalls ein Accessed-Bit auf. Statt des Expand-Down-Bits (Bit E) der Datensegment-Deskriptoren besitzt ein Codesegment-Deskriptor das Bit C (Conforming). Programmsegmente, die als Conforming gekennzeichnet sind, können auch von weniger privilegierten Codesegmenten direkt angesprochen werden, ohne daß der Umweg über ein Gate notwendig ist. Der Code des Segments wird dann mit der Privilegierungsstufe des aufrufenden Segments ausgeführt. Typischerweise befinden sich Systemfunktionen, die keine geschützten Teile des Systems benützen, in Conforming-Segmenten. Damit können Anwendungsprogramme auf weniger kritische Funktionen zugreifen, ohne den langwierigen Umweg über ein Call-Gate machen zu müssen.
|