Es gibt zwei Hauptgründe.
Der erste Grund ist, dass die x86-CPUs zwar vier Speicherringe bieten, die dabei angebotene Granularität des Schutzes jedoch nur auf Segmentebene liegt. Das heißt, jedes Segment kann auf einen bestimmten Ring ("Berechtigungsstufe") von 0 bis 3 eingestellt werden, zusammen mit anderen Schutzfunktionen wie "Schreibgeschützt". Es sind jedoch nicht so viele Segmentdeskriptoren verfügbar. Die meisten Betriebssysteme wünschen sich eine viel feinere Granularität des Speicherschutzes. Wie ... für einzelne Seiten.
Geben Sie den Schutz basierend auf Seitentabelleneinträgen (PTEs) ein. Die meisten, wenn nicht alle modernen x86-Betriebssysteme ignorieren den Segmentierungsmechanismus mehr oder weniger (so weit sie können) und verlassen sich auf PTE-basierten Schutz. Dies wird durch Flag-Bits angegeben, die die unteren 12 Bits in jedem PTE sind - plus Bit 63 auf CPUs, die No-Execute unterstützen. Für jede Seite gibt es einen PTE, normalerweise 4 KB.
Eines dieser Flag-Bits wird als "privilegiertes" Bit bezeichnet. Dieses Bit steuert, ob sich der Prozessor in einer der "privilegierten" Ebenen befinden muss, um auf die Seite zuzugreifen. Die "privilegierten" Ebenen sind PL 0, 1 und 2. Aber es ist nur ein Bit, so dass auf der Seite-für-Seite-Schutzstufe die Anzahl der "Modi", die für den Speicherschutz verfügbar sind, nur zwei beträgt: Eine Seite kann vom nicht privilegierten Modus aus zugänglich sein oder nicht. Daher nur zwei Ringe.
Um vier mögliche Ringe für jede Seite zu haben, müssten sie zwei Schutzbits in jedem Seitentabelleneintrag haben, um eine von vier möglichen Ringenummern zu codieren (genau wie die Segmentdeskriptoren). Sie tun es nicht.
Der zweite Grund ist das Ziel der Betriebssystemportabilität. Es geht nicht nur um x86; Unix hat uns beigebracht, dass ein Betriebssystem für mehrere Prozessorarchitekturen relativ portabel sein kann, und das war eine gute Sache. Einige Prozessoren unterstützen nur zwei Ringe. Indem die Betriebssystemimplementierer nicht von mehreren Ringen in der Architektur abhängig waren, machten sie die Betriebssysteme portabler.
Es gibt einen dritten Grund , der für die Windows NT-Entwicklung spezifisch ist. Die Designer von NT (David Cutler und sein Team, die Microsoft von DEC Western Region Labs engagiert hat) hatten bereits umfangreiche Erfahrungen mit VMS gesammelt. Tatsächlich gehörten Cutler und einige der anderen zu den ursprünglichen Designern von VMS. Und der VAX-Prozessor, für den VMS entwickelt wurde (und umgekehrt), hat vier Ringe. VMS verwendet vier Ringe. (Tatsächlich verfügt der VAX über vier Schutzbits im PTE, die Kombinationen wie "Nur Lesen aus dem Benutzermodus, aber aus Ring 2 und Innerem beschreibbar" ermöglichen. Aber ich schweife ab.)
Die Komponenten, die in den VMS-Ringen 1 und 2 (Record Management Services bzw. CLI) ausgeführt wurden, wurden jedoch im NT-Design nicht berücksichtigt. Bei Ring 2 in VMS ging es nicht wirklich um die Sicherheit des Betriebssystems, sondern darum, die CLI-Umgebung des Benutzers von einem Programm zum nächsten zu erhalten, und Windows NT hatte dieses Konzept einfach nicht. Die CLI wird wie gewohnt ausgeführt. Was den Ring 1 von VMS betrifft, musste der RMS-Code in Ring 1 ziemlich oft in Ring 0 rufen, und Ringübergänge sind teuer. Es stellte sich als weitaus effizienter heraus, einfach zu Ring 0 zu gehen und damit fertig zu werden, anstatt viele Ring 0-Übergänge innerhalb des Ring 1-Codes zu haben. (Wieder - nicht, dass NT sowieso so etwas wie RMS hat.)
Aber warum sind sie dann dort? Warum x86 vier Ringe implementiert hat, während Betriebssysteme sie nicht verwendeten - Sie sprechen von Betriebssystemen mit weitaus neuerem Design als x86. Viele der "Systemprogrammierungs" -Funktionen von x86 wurden lange vor der Implementierung von NT- oder echten Unix-Kerneln entwickelt, und sie wussten nicht wirklich, was die Betriebssysteme verwenden würden. (Es war nicht , bis wir Paging auf x86 bekamen - das erst in den 80386 zeigen hat - , dass wir können . Wahren Unix-ish oder VMS-ähnlichen Kernel ohne Umdenken Speicherverwaltung von Grunde auf neu implementieren)
Moderne x86-Betriebssysteme ignorieren nicht nur die Segmentierung weitgehend (sie richten nur die C-, D- und S-Segmente mit der Basisadresse 0 und einer Größe von 4 GB ein; F- und G-Segmente werden manchmal verwendet, um auf wichtige Betriebssystemdatenstrukturen zu verweisen), sondern auch Dinge wie "Taskstatus-Segmente" weitgehend ignorieren. Der TSS-Mechanismus wurde eindeutig für das Umschalten des Thread-Kontexts entwickelt, hat jedoch zu viele Nebenwirkungen, sodass moderne x86-Betriebssysteme dies "von Hand" tun. Das einzige Mal, wenn x86 NT Hardwareaufgaben ändert, sind beispielsweise einige wirklich außergewöhnliche Bedingungen, wie z. B. eine Doppelfehlerausnahme.
Bei x64 wurden viele dieser nicht mehr genutzten Funktionen weggelassen. (Zu ihrer Ehre sprach AMD tatsächlich mit OS-Kernelteams und fragte, was sie von x86 brauchten, was sie nicht brauchten oder nicht wollten und was sie hinzufügen möchten.) Segmente auf x64 existieren nur in dem, was sein könnte Als Restform bezeichnet, gibt es keine Taskstatusumschaltung usw. Und Betriebssysteme verwenden weiterhin nur zwei Ringe.