Wie verfolgt Windows OS den virtuellen Speicher eines Prozesses?

555
Boagz

Hintergrund: Ich weiß also, dass ein Prozess in modernen Betriebssystemen grundsätzlich über einen vollständigen 32-Bit- (oder 64-Bit-) Adressraum verfügt, mit dem gearbeitet werden kann. Soweit ich weiß, werden die tatsächlichen virtuellen Speicheradressen für den Prozess zunächst in der EXE-Datei des Programms gespeichert. Das bedeutet, dass der Linker alle Funktionsreferenzen und Variablen mit tatsächlichen Adressen wie 0x00007fb6 patchen soll. Nach meinem Verständnis können diese virtuellen Adressen (oder eine modifizierte Version von ihnen aufgrund des Laders) schließlich tatsächlichen physischen Adressen im RAM zugeordnet werden.

Problem: Meine Frage ist, wenn Autoren über Dinge wie das Mappen von etwas in den virtuellen Adressraum eines Programms sprechen, z. B. das Mappen von Dateiinhalten. Was genau bedeuten sie? Soweit ich es mir vorstellen kann, erhält das Betriebssystem die virtuelle Adresse eines einzelnen Prozesses aus der EXE-Datei. Wenn also der Inhalt einer anderen Datei in den virtuellen Adressraum dieses Prozesses eingefügt wird, würden diese Inhalte dann in der Exe-Datei gespeichert werden. Oder verfolgt das Betriebssystem die hinzugefügten Inhalte nur ein wenig wie?

2
Eine "Exe-Datei" wird als schreibgeschützt behandelt. Nach dem ersten Laden werden die Prozessmetadaten alle im Speicher des Kernels gespeichert (und beim Beenden des Prozesses verworfen). Holen Sie sich ein Windows Internals-Buch, wenn Sie die blutigen Details wollen. Bob vor 6 Jahren 0
Alternativ können Sie die [Linux-Dokumentation] (https://www.kernel.org/doc/gorman/html/understand/understand007.html) betrachten. Der Windows-Kernel ist ziemlich ähnlich (innere Strukturen und Layout unterscheiden sich, sind aber allgemein Methoden sind die gleichen). Bob vor 6 Jahren 0

1 Antwort auf die Frage

3
Bob

Erstens, wenn Sie sich eingehend mit dieser Art von Dingen beschäftigen möchten, würde ich dringend empfehlen, sich eine Kopie von Windows Internals, Teil 1: Systemarchitektur, Prozesse, Threads, Speicherverwaltung und mehr, 7. Ausgabe, zu holen . Es macht eine gute Arbeit, die Details zu erklären. Alternativ können Sie sich beispielsweise die Linux-Dokumentation anschauen, die öffentlich zugänglich ist - sie wird ein wenig anders sein, folgt jedoch den gleichen Konzepten wie Windows.

Hier ist eine andere, vielleicht zugänglichere Erklärung.

Vielleicht möchten Sie auch einen Blick auf die Funktionsweise von Seitentabellen und möglicherweise auch auf TLBs werfen .


Wenn ein Prozess versucht, eine bestimmte Seite seines virtuellen Adressraums zu lesen oder zu schreiben, übersetzt die Speicherverwaltungseinheit der CPU (MMU) der CPU diese entweder in eine physische Adresse (indem sie die Seitentabellen über den TLB betrachtet, einen speziellen On-Chip-Cache (nur für Seitentabelleneinträge) oder wenn der Seitentabelleneintrag "Seite nicht vorhanden" sagt, benachrichtigt das Betriebssystem. Das nennt man einen Seitenfehler . In Reaktion darauf weist das Betriebssystem a) dem Prozess gegebenenfalls eine neue physische Seite zu und lädt seinen Inhalt vom "Backing Store" (Auslagerungsdatei oder einer zugeordneten Datei) in diese Seite des RAM, b) aktualisiert den Seitentabelleneintrag, um den physischen Inhalt anzuzeigen Adresse der Seite und c) fordert die CPU auf, es erneut zu versuchen ("lehnt den Seitenfehler ab").

Für jede Seite des definierten virtuellen Adressraums gibt es einen Seitentabelleneintrag. Die Seitentabelleneinträge werden nicht nur von der CPU zur Adressübersetzung verwendet. PTEs, die "ungültigen" Seiten entsprechen - also solche, die beim Zugriff zu einem Seitenfehler führen würden - werden vom Betriebssystem zum Speichern von Informationen über den Ort der Seitendaten verwendet. Somit existieren PTEs für alle definierten virtuellen Adressen. Eine Teilmenge ist normalerweise "gültig", dh sie entspricht virtuellen Adressen, auf die ohne Seitenfehler zugegriffen werden kann. (Diese Teilmenge wird als "Arbeitssatz" des Prozesses bezeichnet.)

Die PTEs für "ungültige" Seiten enthalten Informationen, anhand derer das Betriebssystem den Inhalt der Seite finden kann, wenn diese Seite fehlerhaft ist. Der Inhalt kann sich in einer Seitendatei, in einer zugeordneten Datei, im RAM (in einem der Seitenzwischenspeicher), in einem Arbeitssatz eines anderen Prozesses (für gemeinsam genutzte Seiten) befinden oder manchmal gar nirgends (für Seiten, die keinen ursprünglichen Inhalt hatten definiert und noch nicht referenziert worden ist, solche Seiten werden einfach physisch zugewiesen und bei der ersten Referenz mit Nullen gefüllt; diese Ereignisse werden als "Bedarf-Null-Seitenfehler" bezeichnet.

Seitentabellen sind zu groß, um vollständig in den (sehr, sehr kleinen) TLB zu passen, weshalb sie nur im normalen Speicher abgelegt werden.

Seitentabellen sind tatsächlich zu groß, um alle gleichzeitig in den RAM-Speicher zu passen. Sie sind also pro Prozess in einer Baumstruktur organisiert, und alle Tabellen mit Ausnahme der obersten Tabelle jedes Baums sind selbst pagable.

In Ihrem typischen modernen Betriebssystem verfügt jeder Prozess über eine eigene Seitentabelle, die den Adressraum des Prozesses unabhängig voneinander hält. Der Kernel verfolgt Prozesse und ihre Adressräume in seinen eigenen Speicherbereichen. Im aktuellen Windows wird dies zusammen mit anderen Prozessmetadaten im Speicher in einer EPROCESSDatenstruktur " " und zugehörigen Strukturen, den so genannten virtuellen Adressbeschreibungen, gespeichert . Windows unterhält auch separate Seitentabellenstrukturen für seinen eigenen Kernelmoduscode und Daten.

Beachten Sie Folgendes: Der Adressraum eines Prozesses wird nur im Arbeitsspeicher gespeichert und niemals in die ausführbaren Dateien + DLLs zurückgeschrieben, von denen aus der Prozess gestartet wurde. Der Adressraum wird verworfen, wenn der Prozess beendet wird. Änderungen an zugeordneten Dateien, die für den Schreibzugriff geöffnet wurden, werden jedoch auf ihre jeweiligen Dateien zurückgesetzt.


Soweit ich weiß, werden die tatsächlichen virtuellen Speicheradressen für den Prozess zunächst in der EXE-Datei des Programms gespeichert. Das bedeutet, dass der Linker alle Funktionsreferenzen und Variablen mit tatsächlichen Adressen wie 0x00007fb6 patchen soll.

Das gilt nur für Verweise auf Programmcode. Der Adressraum eines Prozesses enthält außerdem zwei oder mehr Heaps, einen Stack für jeden Thread und alle zusätzlichen Adressbereiche, die der Thread (die Threads) des Prozesses während der Ausführung erstellt.

Heutzutage wird sogar Programmcode beim Laden (zufällig) neu angeordnet. Dies ist als ASLR bekannt und macht bestimmte Angriffe schwieriger.

Soweit ich es konzeptualisieren kann, erhält das Betriebssystem die virtuelle Adresse eines einzelnen Prozesses aus der EXE-Datei

Nein. Eine ausführbare Datei gibt nur den Ort einiger Anfangsdaten an. nämlich der Programmcode, globale Variablen und verknüpfte Bibliotheken. Und sogar viele dieser Informationen werden von ASLR in modernen Betriebssystemen überschrieben.

Andere Speicher (Programmspeicher, Thread-Stacks, zugeordnete Dateien usw.) werden vom Programm dynamisch zugewiesen. Ihre Größe, Position usw. können und werden sich während der Programmausführung ändern.

Wie oben erwähnt, hat jeder Prozess seinen eigenen unabhängigen virtuellen Adressraum. Ein Teil davon ist dem Kernel-Speicher zugeordnet (der im Benutzermodus als nicht lesbar oder änderbar gekennzeichnet ist), der Rest ist jedoch privat für den Prozess. Keiner der Adressräume eines Prozesses wird mit anderen Prozessen gemeinsam genutzt, sofern dies nicht explizit angefordert wird ("Shared Memory") oder die Performance vollständig transparent ist (Copy-on-Write-Dateien, zugeordnete Dateien).

Abbildung 5-10 x86 virtuellen Adressraum - Layout (2 GB auf der linken Seite, 3 GB auf dem rechten Seite ) von Windows Internals, Teil 1: Systemarchitektur, Prozesse, Threads, Speicherverwaltung und vieles mehr, 7. Auflage ist ein gutes Diagramm das zeigt, allgemeines Layout des Prozessadressraums. Leider darf ich es hier nicht reproduzieren.

Wenn also der Inhalt einer anderen Datei in den virtuellen Adressraum dieses Prozesses abgebildet wird

Dies endet als spezieller Eintrag in einer Seitentabelle eines Prozesses, der das Betriebssystem anweist, Daten aus einer zugeordneten Datei zu laden. Es ist effektiv eine Weiterleitung. ein "anderswo suchen". Denken Sie daran, dass der Adressraum virtuell ist - er kann sich auf eine Datei beziehen, diese Datei wird jedoch erst geladen, wenn ein Thread versucht, sie zu lesen / schreiben. Und selbst dann werden nur eine oder mehrere Seiten gleichzeitig geladen.

Dann würden diese Inhalte in die Exe-Datei zurückgespeichert

Ausführbare Dateien werden immer schreibgeschützt geladen. Nach dem Laden werden alle relevanten Informationen rein im Speicher gespeichert und vom Betriebssystem niemals "zurückgespeichert". Alle Änderungen am Prozessspeicher gehen beim Beenden des Prozesses verloren, mit Ausnahme von Schreibvorgängen in zugeordnete Dateien oder in den gemeinsam genutzten Speicher.

Oder verfolgt das Betriebssystem die hinzugefügten Inhalte nur ein wenig wie?

Ja, welche Regionen werden welcher Datei in den Prozessmetadaten zugeordnet.

Zum Bearbeiten: Wenn die MMU versucht, eine virtuelle Seitennummer zu übersetzen, wird der benötigte PTE automatisch in den TLB eingefügt, wenn der benötigte PTE nicht bereits im TLB zwischengespeichert ist. Das Betriebssystem schreibt diese Informationen nicht direkt in den TLB (und es gibt keinen Grund dafür). Das Betriebssystem kann der CPU jedoch mitteilen, dass der TLB für eine bestimmte virtuelle Seitennummer _invalidiert_ wird; Dies geschieht immer dann, wenn der Inhalt eines PTE geändert wird. Das Laden des geänderten PTE in den TLB wird jedoch nur von der MMU auf Anforderung durchgeführt, wenn die vom PTE beschriebene virtuelle Seite übersetzt wird. Jamie Hanrahan vor 6 Jahren 0
@ JamieHanrahan Danke für die Bearbeitung. Zugegeben, die einzige praktische Erfahrung, die ich mit der OS-Speicherverwaltung gemacht habe, war System / 161 (MIPS), was sich etwas von x86 unterscheiden würde. Insbesondere hat MIPS einen von der Software verwalteten TLB, während x86 anscheinend von der Hardware verwaltet wird (und wenn ich dies richtig lese, wechselt seine Seite auf die Seitentabelle, auf die ein Steuerregister verweist?). Bob vor 6 Jahren 0
Ja. CR3 enthält die physikalische Adresse der Seitentabelle der obersten Ebene (auf x86 "Seitenverzeichnis", auf x86 "page directory pointer table" mit PAE, auf x64 "page map 4-Tabelle"). Jeder Prozess hat einen anderen Baum von Seitentabellen und daher einen anderen Wert für CR3. Ein Prozesskontextwechsel umfasst das Laden von CR3 mit dem richtigen Wert für den neuen Prozess. Dies hat den Nebeneffekt, dass alle Einträge im TLB für ungültig erklärt werden, bei denen das Flag "G" (global) nicht gesetzt ist (als "Translation Buffer Shootdown" bezeichnet). Interessant über die MIPS, das wusste ich nicht! Jamie Hanrahan vor 6 Jahren 0
Übrigens, wenn Sie sich ein System oder einen Dump mit windbg anschauen, zeigt das Feld "DirBase" in der Prozessausgabe! den Inhalt von CR3. Jamie Hanrahan vor 6 Jahren 0
Danke für die Antwort. Nur um die Konzepte hier zu verfestigen, sagen Sie, dass im eigentlichen RAM Speicherplatz für den Kernel reserviert ist, um den virtuellen Adressraum aller Prozesse zu verfolgen. Wenn also ein Prozess beispielsweise einen Speicherbereich reserviert, speichert der Kernel diese Informationen im physischen RAM, das später nachgeschlagen werden kann, wenn der Prozess dann diesen reservierten Speicherplatz festschreiben und verwenden muss. Boagz vor 6 Jahren 0
@Boagz Mehr oder weniger, ja. Der gesamte Adressraum wird immer verfolgt (um Platz zu sparen, in einer mehrstufigen Seitentabelle [größere Blöcke können in einem einzelnen Eintrag auf einer höheren Ebene als nicht verwendet markiert werden.) (Https://stackoverflow.com/questions/29467510) statt einzelner Einträge für jede Seite). [Reservieren und festschreiben sind spezifische Vorgänge in Windows] (https://msdn.microsoft.com/de-de/library/windows/desktop/aa366916), aber mit der Seitentabelle wird nachverfolgt, welche Seiten frei sind / reserviert / festgelegt / verwendet. Nicht sicher, ob reserviert / festgeschrieben (aber nicht verwendet) pro Seite oder pro Seitenblock verfolgt wird. Bob vor 6 Jahren 1
Für den virtuellen Adressraum werden reservierte, festgeschriebene und zugeordnete Bereiche über virtuelle Adressdeskriptoren verfolgt. Nur festgeschriebene und zugeordnete Seiten, auf die tatsächlich zugegriffen wurde, haben notwendigerweise Seitentabelleneinträge für ihre Seiten. Es gibt kein VAD für einen nicht verwendeten Bereich. Physische Seiten (RAM) werden über eine Vielzahl von Strukturen verfolgt, vor allem über das "PFN-Array", das systemweite Seitenlisten (nullte, frei, standby und modifiziert) implementiert und die Verwendung jeder physischen Seite verfolgt. In Prozessen gültige Seiten werden über pro-Prozess-Listen, so genannten Arbeitssatzlisten, verfolgt. Jamie Hanrahan vor 6 Jahren 1