2010-11-22 4 views
3

I < Windows über C/C++ 5. Auflage lese> und unten ist einige Zitat:Frage zum NULL-Pointer Zuordnung Partition in einem Prozess Adressraum

Jeder Prozess virtuelle Adressraum ist Split in Partitionen. Auf x86 32-Bit Windows, die Partition von 0x00000000 - 0x0000FFFF (inklusive) heißt NULL-Zeiger Zuordnung Partition. Diese Partition wird beiseite gestellt, Programmierer fangen NULL-Zeiger Zuordnungen. Wenn ein Thread in Ihrem Prozess versucht, von oder in eine Speicheradresse in dieser Partition zu lesen, wird eine Zugriffsverletzung ausgelöst.

Ich frage mich, warum müssen wir statt einen Bereich von Adressraum nur den Wert 0 NULL-Zeiger-Zuweisungen zu fangen? AFAIK, NULL ist 0. Also, was ist die Überlegung hinter diesem Design? Gibt es noch etwas in diesem Bereich, das der Benutzer nicht berühren sollte? Oder ist NULL nicht notwendigerweise gleich 0?

Vielen Dank.

Antwort

4

Alle modernen Betriebssysteme implementieren clevererweise eine Nullzeiger-Dereferenzierungsprüfung ohne Ausführungsaufwand mit der virtuellen Speicherverwaltungshardware. Die 0-te 4-kB-Seite (oder in diesem Fall die 0-15-te Seite (insgesamt 64 kB)) wird eingerichtet, so dass jedes Lesen, Schreiben oder Ausführen von Daten an Adresse 0 (oder 0x00000FFF oder 0x0000FFFF) sofort zu einem Zugriff führt Verstoß Ausnahme.

Lassen Sie mich es anders sagen. Das Abfangen von Nullzeiger-Dereferenzierungen auf 0, aber das Zulassen von Datenreferenzen auf (etwa) Adresse 1 wäre unerschwinglich teuer - da Sie die Seitengranularitäts-VM-Hardware nicht verwenden könnten, wäre es stattdessen notwendig, eine Nullzeigervergleichs- und Verzweigungssequenz durchzuführen vor vielen Zeigerdereferenzen. Indem die erste Seite oder die ersten Seiten nicht zugänglich gemacht werden, kann diese Überprüfung "kostenlos" in der VM-Hardware durchgeführt werden. Der Nachteil ist, dass Sie diese erste (n) Seite (n) nicht für etwas anderes verwenden können.

(Theoretisch könnte ein ausreichend optimiert Compiler dann verwendet werden, um festzustellen, welche Zeiger Dereferenzierungen konnte nicht möglicherweise Null-Zeiger dereferenziert sein, aber bis zu dem cleveren Compiler erscheinen, die nicht zugeordneten 0-Seite Trick zu tun hat.)

Also warum ist die Windows (post Win98) Nullzeigerzuweisungs-Partition 64 KB statt der minimal notwendigen 4 KB? Die Antwort von Wyzard ist ein guter Fall für das Einfangen von Null-Pointer-Array-Indexfehlern.

* Späte Aktualisierung: RE: warum 64 KB?- Ich habe jemanden gefragt, der über das Windows Core-Team Bescheid wissen sollte, und sie sagten, dass es sich wahrscheinlich um eine 64-KB-Region handelt, da Windows bestimmte Speicherverwaltungsstrukturen mit einer Zuordnungsgranularität von 64 KB speichert. Warum das? Ich weiß es nicht, aber vielleicht hat Raymond Chen die Antwort: http://blogs.msdn.com/b/oldnewthing/archive/2003/10/03/55239.aspx. Beeindruckend. *

+0

Danke für Ihre Antwort. Es scheint, dass die Ursache in technischen Details liegt, nicht in reiner Logik. – smwikipedia

3

Innerhalb der C- oder C++ - Sprache ist NULL immer gleich 0, aber 0 als Zeigerwert im Quellcode entspricht nicht unbedingt allen Null-Bits in der kompilierten Binärdatei. Unter Windows glaube ich jedoch, dass ein Nullzeiger wirklich alle Null-Bits ist.

Der reservierte Raum ist wahrscheinlich fangen Dinge wie myarray[n] wo myarray null ist, oder mystruct->myfield wo mystruct null ist zu helfen. Die Adresse, auf die zugegriffen wird, ist nicht notwendigerweise genau dort, wo der Zeiger zeigt, er kann irgendwo danach liegen.

+0

Vielen Dank für Ihre Antwort. 0xFFFF ist 64 KB groß. Wenn ich versuche, auf myarray [64K + 1] zuzugreifen, wo myarray null ist, wird dies abfangbar sein? Ich verstehe nicht ganz, warum * 0 als Zeigerwert im Quellcode nicht notwendigerweise allen Null-Bits in der kompilierten Binärdatei * entspricht. Könnten Sie dazu noch mehr Erläuterungen geben? Vielen Dank. – smwikipedia

+0

@smwikipedia, ich denke, dass wäre nicht abfangbar, obwohl ich es nicht versucht habe. Ich weiß nicht, was bei 0x10000 im Adressraum ist. Betrachten Sie für die Darstellung von Nullen eine hypothetische Plattform, auf der ein "Nullzeiger" durch die Speicheradresse 0xFFFFFFFF dargestellt wird, da 0x00000000 einige hypothetische wichtige plattformspezifische Daten enthält. Sie würden immer noch 0 (oder NULL) in den Quellcode schreiben, und der Compiler würde das im Maschinencode in 0xFFFFFFFF übersetzen. – Wyzard

+0

Wyzard, Sie könnten amüsiert sein zu wissen, dass in bestimmten Fällen in Microsoft C++ Nullzeiger auf Datenelemente (eine ziemlich seltsame Art von Zeiger) in Wirklichkeit als das -1 All-Einsen-Bitmuster dargestellt werden. –

0

Der Bereich ist erforderlich, um die Erkennung von Fehlern zu erleichtern, die durch Dereferenzierung von null und nahe Nullzeiger verursacht werden. Zum Beispiel:

int startOffset = 1000; //start at 1000th element; 
char* buffer = obtain();// happens to be null 
for(int i = startOffset; buffer[i] != 0; i++) { 
    //do stuff 
} 

Hinweis Thet Nullzeiger wird in dem obigen Beispiel nie dereferenziert. Aber da buffer ein Nullzeiger ist, sind die resultierenden Adressen nahe null und Dereferenzierung kann leicht mit der "Partition" -Technik abgefangen werden.

0

Ein Bereich ist erforderlich, da bei der Dereferenzierung eines Zeigers auf eine Struktur oder ein Klassenelement der tatsächlich aufgerufene Speicher der Zeiger plus der Offset des Elements in der Struktur ist.

struct Foo { 
    int a; 
    inb b; 
}* Bar = 0; 

Bar->b = 0; 

On int = 32-Bit-Compiler, wird das 'b' Element 4 Bytes in den struct sein, so Bar-> b, um die Adresse 0x00000004 zuzugreifen versucht.

Verwandte Themen