2010-11-22 11 views
3

私はC++/Cを経由して第5版を<のWindowsを読み>と以下のいくつかの引用です午前:質問のアドレス空間

各プロセスの仮想アドレス空間は 分割され、パーティションに分割します。 x86 32ビットの場合 Windowsの場合、0x00000000〜 0x0000FFFF(これを含む)のパーティションは、 NULLポインタ割り当てパーティションと呼ばれます。 このパーティションは、 プログラマがNULLポインタ 割り当てをキャッチするのを助けるために用意されています。 プロセス内のスレッドが、この パーティション内のメモリアドレスに書き込むか、 の書き込みを試みると、アクセス違反は になります。

私は疑問に思って、なぜ我々は範囲のアドレス空間の代わりに、がNULLポインタ割り当てをキャッチするだけの価値のを使用する必要がありますか? AFAIK、NULLは0です。だから、このデザインの背後にある考慮点は何ですか?ユーザーが触れてはいけない範囲が他にありますか?または、NULLは必ずしも0とは限りませんか?

多くのありがとうございます。

答えて

4

現代のオペレーティングシステムはすべて、仮想メモリ管理ハードウェアを使用して実行オーバーヘッドなしでヌルポインタ逆参照チェックを賢明に実装しています。 0番地(または0x00000FFF番地または0x0000FFFF番地)のデータ読出しまたは書込みまたは命令実行が即時にアクセスできるように、0番目の4KBページ(この場合は0〜15ページ(合計64KB))が設定されています違反例外。

私はそれを別の方法で入れてみましょう。 NULLポインタの逆参照を0にトラップするが、データ参照を(たとえば)アドレス1にすることは、非常に高価になります。ページ・グラニュラリティVMハードウェアを使用できないため、代わりにNULLポインタの比較と分岐シーケンスを実行する必要があります多くのポインタ逆参照の前。最初のページまたは最初の数ページをアクセス不能にすることで、このチェックはVMハードウェアで「無料」で実行できます。欠点は、最初のページを他のものに使用できないことです。 (理論的には、十分に最適化コンパイラは、その後、逆参照はおそらくNULLポインタデリファレンスすることができませんでしたが、そのような賢いコンパイラが表示されるまで、マップされていない0ページトリックを行う必要がありますどのポインタを決定するために使用することができる。)

なぜ、Windows(ポストWin98)のヌルポインタ割り当てパーティションが、必要最小限の4 KBの代わりに64 KBですか? Wyzardの答えは、NULLポインタ配列のインデックスエラーも捕捉する良いケースになります。

*最新の更新:RE:なぜ64 KBですか? - Windows Coreチームで知っておくべき人物を尋ねました。Windowsは特定のメモリ管理構造を64 KBの割り当て単位で保持しているため、おそらく64 KBの領域と言います。何故ですか?私は知らないが、おそらくレイモンド・チェンは答えを持っている:http://blogs.msdn.com/b/oldnewthing/archive/2003/10/03/55239.aspx。ワオ。 *

+0

ご返信ありがとうございます。根本的な原因は、純粋な論理ではなく、エンジニアリングの詳細にあるようです。 – smwikipedia

3

CまたはC++言語では、NULLは常に0に等しくなりますが、ソースコード内のポインタ値としての0は、必ずしもコンパイル済みバイナリのすべてのゼロビットに対応するとは限りません。しかし、Windows上では、ヌルポインタは実際にはすべて0ビットと考えています。

myarray[n]ここでmyarrayがヌルの場合、またはmystruct->myfieldの場合、予約されたスペースはおそらくmystructがヌルです。アクセスされるアドレスは、必ずしもポインタが指す場所ではなく、後のどこかにある可能性があります。

+0

ご返信ありがとうございます。 0xFFFFのサイズは64Kです。 myarrayがnullのmyarray [64K + 1]にアクセスしようとすると、これは捕まえることができますか?私は、ソースコード内のポインタ値として* 0がコンパイルされたバイナリのすべてのゼロビットに必ずしも対応しない理由を理解していません。ありがとう。 – smwikipedia

+0

@smwikipedia、私はそれを試していないが、捕まえられないだろうと思う。私はアドレス空間の0x10000に何があるのか​​分からない。ヌルの表現に関して、0x00000000は仮想的に重要なプラットフォーム固有のデータを保持しているため、「ヌルポインタ」がメモリアドレス0xFFFFFFFFで表される仮想的なプラットフォームを想像してください。あなたはまだ0(またはNULL)をソースコードに書いています。コンパイラはそれをマシンコードの0xFFFFFFFFに変換します。 – Wyzard

+0

Wyzardの場合、Microsoft C++の特定のケースでは、データメンバーへのヌルポインター(むしろ興味のある種​​類のポインター)が実際に-1すべてのもののビットパターンとして表されていることに気をつけているかもしれません。 –

0

この範囲は、ヌルポインタの逆参照によって発生するバグの検出を容易にするために必要です。たとえば:

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

注thetのヌルポインタが上記のサンプルで間接参照されることはありません。しかし、bufferはnullポインタに近いヌルポインタにあり、それらを逆参照することは簡単に "パーティション"テクニックを使用してキャッチすることができます。

0

structまたはclass要素へのポインタが逆参照されるとき、アクセスされる実際のメモリはポインタであり、構造体内のメンバのオフセットになるため、範囲が必要です。 INT = 32ビットコンパイラで

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

Bar->b = 0; 

バー - > Bは、アドレス0x00000004にアクセスしようとするので、「B」部材は、構造体に4つのバイトであろう。

関連する問題