2016-12-19 6 views
1

Dies ist keine Frage zu einem bestimmten Betriebssystem, aber nehmen wir Windows als Beispiel. Ein Userspace-Programm verwendet die Windows-API für die Kommunikation mit Kernelspace. Ich verstehe jedoch nicht, wie das möglich ist. Die API lebt gemäß MS-Websites im Benutzerbereich. Um auf Kernelspace zugreifen zu können, muss es im Kernelspace sein, wenn ich es richtig verstehe. Was also ist der Mechanismus, mit dem die Windows-API zusätzliche Berechtigungen erhält, um mit dem Kernelspace zu sprechen? In welchem ​​Raum funktioniert dieser Mechanismus? Ist diese Art von Ding universell für alle modernen PC-Betriebssysteme?Wie ermöglichen Betriebssysteme Userspace-Programmen die Interaktion mit Kernelspace-Programmen?

+1

Suchen Sie nach "Systemaufrufen". – glauxosdever

+0

Hallo, ich weiß, dass der Mechanismus * ein Systemanruf * genannt wird. Meine Frage bezieht sich darauf, wie ein Betriebssystem Informationen zwischen den zwei Speicherplätzen übertragen kann. Ein Systemaufruf ist vermutlich eine Funktion, die in ein Programm kompiliert wird, das im Benutzerbereich ausgeführt wird. Es sollte keinen Zugriff auf Kernelspace haben. Warum geht das? Gibt es einen dritten Raum, der zum Beispiel intermediär ist? –

+0

@MichaelStachowsky Fühlen Sie sich frei für irgendwelche Fragen. –

Antwort

1

Es ist die CPU, die als Zwischenspeicher für die Übertragung von Informationen zwischen Benutzerspeicherbereich (im Benutzermodus zugänglich) auf geschützten Speicherbereich (im Kernelmodus zugänglich) über CPU-Register fungiert.

Hier ist ein Beispiel:

Angenommen, ein Benutzer ein Programm in höherer Sprache schreibt. Wenn nun die Ausführung des Programms erfolgt, erzeugt die CPU die virtuellen Adressen.

Jetzt wird vor dem Lesen/Schreiben die virtuelle Adresse in die physikalische Adresse konvertiert. Da der Übersetzungsmechanismus (Speicherverwaltungseinheit) nur im Kernel-Modus zugänglich ist, weil er im geschützten Speicher gespeichert ist, findet die Übersetzung im Kernel-Modus statt und die physikalische Adresse wird schließlich in einigen Registern der CPU gespeichert. Schreibvorgang tritt auf.

+0

Ich sehe. Aber wie geht die Kommunikation in die andere Richtung? Ich bekomme, dass die Kernelspace-Programme zurück zum Userspace kommunizieren können. Sind die Register nicht geschützt? Was passiert, wenn keine verfügbar sind? –

+1

Allgemeine Register sind nicht geschützt, sie sind für das Programm dort zu benutzen.Jedes Mal, wenn das Betriebssystem Tasks wechselt, werden Register der alten Task gespeichert und Register der neuen Task werden wiederhergestellt. Außerdem würde es die Programme nicht erlauben, Register von einem Speicherort zu einem anderen zu verschieben ('mov [loc2], [loc1]' ist ungültig), während alles viel langsamer gemacht wird (das Programm müsste das tun) Zugriff auf den Speicher für fast jede Operation). – glauxosdever

+0

Diese Antwort übersieht die tatsächlichen verwendeten Systemaufrufmechanismen und ist nicht sehr genau bei der Beschreibung der Übertragung von Daten unter Verwendung von Registern oder der Operation einer MMU, die für die Frage ohnehin nicht besonders relevant ist. – Flexo

2

Wie Sie bereits wissen, gibt es eine Reihe von Einrichtungen, die von Windows-Kernel-Programmen zur Verfügung gestellt werden. (Wenn Sie neugierig sind there's a list of system calls). Diese Systemaufrufe werden alle durch eine eindeutige Nummer identifiziert, die nicht Teil der öffentlich dokumentierten Schnittstelle von Microsoft ist. Wenn Sie jedoch eine öffentlich zugängliche Funktion von Ihrem Programm aufrufen, ist eine DLL installiert, wenn Sie Windows mit einem Einstiegspunkt installieren (oder aktualisieren), bei dem es sich um einen normalen, nicht privilegierten Benutzermodusfunktionsaufruf handelt. Diese DLL kennt die Zuordnungen zwischen öffentlichen Schnittstellen und den verfügbaren Systemaufrufen im aktuell ausgeführten Kernel. Diese Zuordnungen sind nicht immer 1: 1, was Optimierungen und Verbesserungen ermöglicht, ohne den bestehenden Code mit stabilen Schnittstellen zu unterbrechen.

Wenn ein Benutzerlandcode eine dieser Funktionen aufruft, besteht seine Aufgabe darin, Argumente für den Systemaufruf vorzubereiten und dann den Sprung in den Kernelmodus einzuleiten. Wie genau dieser Sprung erfolgt, hängt von der Architektur ab, auf der Windows derzeit ausgeführt wird. In der Tat variiert es nicht nur zwischen x86 und Arm, sondern auch zwischen AMD und Intel x86 Systemen. Ich werde hier nur aus Gründen der Einfachheit über den modernen 32-Bit-Intel-x86-Fall sprechen (mit dem SYSENTER instruction). Auf x86 sind die meisten anderen Variationen relativ geringfügig, z. B. int 2Eh was used prior to SYSENTER support.

Vor dem Hochfahren bereitet das Betriebssystem eine Menge Arbeit vor, um ein Benutzerland und Systemaufrufe von ihm zu ermöglichen. Dies zu verstehen ist entscheidend, um zu verstehen, wie Systemaufrufe wirklich funktionieren.

Zuerst lassen Sie uns ein wenig zurückspulen und überlegen, was genau wir mit userland und kernelmode meinen. Auf x86 sprechen wir von "Ringen", wenn wir über privilegierten oder nicht-privilegierten Code sprechen. Es gibt tatsächlich 4 (Hypervisoren ignorieren), aber aus verschiedenen Gründen hat niemand wirklich etwas außer Ring0 (Kernel) und Ring3 (Userland) benutzt. Wenn wir Code auf x86 ausführen, stammen die Adresse, die gerade ausgeführt wird (EIP) und die Daten, die gelesen/geschrieben werden, aus Segmenten.

Segmente sind meist nur ein historischer Unfall von den Tagen, bevor die virtuelle Adressierung auf x86 eine Sache war. Sie sind jedoch wichtig für uns hier, weil es spezielle Register gibt, die definieren, welche Segmente gerade verwendet werden, wenn wir Anweisungen ausführen oder anderweitig auf den Speicher verweisen. Segmente auf x86 werden alle in einer großen Tabelle definiert, die Global Descriptor Table oder GDT genannt wird.(Es gibt auch eine lokale Deskriptor-Tabelle, LDT, aber das wird die aktuelle Diskussion hier nicht weiter bringen). Der wichtige Punkt für unsere Diskussion hier ist, dass das (arkane) Layout der Tabelleneinträge 2 Bits enthält, die als DPL bezeichnet werden und die Privilegstufe des gerade aktiven Segments definieren. Sie werden feststellen, dass 2 Bits genau genug sind, um 4 Privilegien zu definieren.

Kurz gesagt, wenn wir über "Ausführen im Kernel-Modus" sprechen, meinen wir nur, dass unsere aktiven Code-Segment (CS) und Datensegment-Selektoren auf Einträge in der GDT zeigen, die DPL auf 0 gesetzt haben eine CS- und Datensegmentselektoren haben, die auf GDT-Einträge zeigen, wobei DPL auf 3 gesetzt ist und kein Zugriff auf Kerneladressen besteht. (Es gibt auch andere Selektoren, aber um es einfach zu halten, betrachten wir erst einmal "Code" und "Daten").

Zurück zu früh während des Bootens des Kernels: Während des Hochfahrens erstellt der Kernel GDT entries we need. (Diese müssen in einer bestimmten Reihenfolge angeordnet sein, damit SYSENTER funktioniert, aber das ist meistens nur ein Implementierungsdetail). Es gibt auch einige "maschinenspezifische Register", die steuern, wie sich unser Prozessor verhält. Diese können nur durch privilegierten Code festgelegt werden. Drei von ihnen, die wichtig sind hier:

  • IA32_SYSENTER_ESP
  • IA32_SYSENTER_EIP
  • IA32_SYSENTER_CS

Daran erinnern, dass wir einen Code haben Tagrunnig in Userland (Ring3), die ring0 den Übergang will . Nehmen wir an, dass es alle Register, die es benötigt, nach der Aufrufkonvention gespeichert hat und Argumente in die richtigen Register legt, die der Aufruf erwartet. Wir treffen dann die SYSENTER-Anweisung. (Eigentlich verwendet es KiFastSystemCall denke ich). Die SYSENTER-Anweisung ist speziell. Es modifiziert die aktuellen Code- und Datensegmentselektoren basierend auf dem Wert, den das Kernel-Setup im maschinenspezifischen Register IA32_SYSENTER_CS festgelegt hat. (Die Stapel-/Datensegmentierungswerte werden als ein Offset von IA32_SYSENTER_CS berechnet). Anschließend wird der Stack-Pointer selbst (ESP) auf den Kernel-Stack gesetzt, der früher für die Behandlung von Systemaufrufen eingerichtet und in den MSR IA32_SYSENTER_ESP und ebenso für EIP der Befehlszeiger von IA32_SYSENTER_EIP gespeichert wurde.

Da der CS-Selector jetzt auf einen GDT-Eintrag zeigt, bei dem DPL auf 0 gesetzt ist und EIP auf Kernelmodus-Code auf einem Kernel-Stack zeigt, laufen wir im Kernel zu diesem Zeitpunkt.

Ab hier kann der Kernel-Modus-Code Speicher aus dem Kernel und dem Benutzerbereich lesen und schreiben (mit entsprechender Vorsicht), um die eigentliche Arbeit auszuführen, die für den Systemaufruf erforderlich ist. Die Argumente für den Systemaufruf können gemäß der aufrufenden Konvention aus Registern usw. gelesen werden, aber alle Argumente, die tatsächlich Zeiger zurück zu Benutzerland oder Handles zu Kernel-Objekten sind, können zum Lesen größerer Datenblöcke ebenfalls aufgerufen werden.

Wenn der Systemaufruf beendet ist, wird der Prozess im Prinzip umgekehrt und wir landen wieder im Benutzerland mit DPL 3 für die Selektoren.

Verwandte Themen