19

Windows アプリケーションは通常、32 ビット システムで 2Gb のプライベート アドレス空間を持っていることが知られています。このスペースは、/3Gb スイッチを使用して 3Gb まで拡張できます。

オペレーティング システムは、残りの 4Gb を予約します。

私の質問はなぜですか?

カーネル モードで実行されるコード (つまり、デバイス ドライバー コード) には、独自のアドレス空間があります。排他的な 4Gb アドレス空間に加えて、オペレーティング システムが各ユーザー モード プロセスの 2Gb を予約したいのはなぜですか?

その理由は、ユーザーモードとカーネルモードの呼び出しの間の移行だと思いました。たとえば、 への呼び出しにNtWriteFileは、カーネル ディスパッチ ルーチンのアドレスが必要です (そのため、システムは各アプリケーションで 2Gb を予約します)。しかし、 を使用するSYSENTERと、システム サービス番号は、カーネル モード コードがどの関数/サービスが呼び出されているかを知るのに十分ではありませんか?

オペレーティング システムが各ユーザー モード プロセスで 2Gb (または 1Gb) を使用することが非常に重要な理由を明確にしていただければ幸いです。

4

5 に答える 5

23

2つの異なるユーザープロセスには、異なる仮想アドレス空間があります。仮想↔物理アドレスのマッピングが異なるため、あるユーザープロセスから別のユーザープロセスにコンテキストを切り替えると、 TLBキャッシュが無効になります。これは非常にコストがかかります。TLBにアドレスがすでにキャッシュされていない場合、メモリアクセスを行うと、障害が発生し、PTEがウォークします。

システムコールには、ユーザー→カーネル、カーネル→ユーザーの2つのコンテキストスイッチが含まれます。これを高速化するために、カーネルで使用するために上位1GBまたは2GBの仮想アドレス空間を予約するのが一般的です。仮想アドレス空間はこれらのコンテキストスイッチ間で変更されないため、TLBフラッシュは必要ありません。これは、各PTEのユーザー/スーパーバイザービットによって有効になります。これにより、カーネルメモリがカーネルスペースにある間だけアクセスできるようになります。ページテーブルが同じであっても、ユーザースペースにはアクセスできません。

2つの別々のTLBのハードウェアサポートがあり、1つはカーネル専用である場合、この最適化はもはや役に立ちません。ただし、専用のスペースが十分にある場合は、TLBを1つ大きくする方がおそらく価値があります。

Linux on x86は、かつて「4G/4Gスプリット」と呼ばれるモードをサポートしていました。このモードでは、ユーザースペースは4GBの仮想アドレス空間全体に完全にアクセスでき、カーネルも4GBの仮想アドレス空間全体に完全にアクセスできます。前述のように、コストは、すべてのsyscallがTLBフラッシュと、ユーザーとカーネルメモリ間でデータをコピーするためのより複雑なルーチンを必要とすることです。これは、最大30%のパフォーマンスペナルティを課すと測定されています。


この質問が最初に尋ねられて答えられてから時代は変わりました:64ビットオペレーティングシステムは今でははるかに普及しています。x86-64の現在のOSでは、 0〜2 47 -1 (0-128TB)の仮想アドレスがユーザープログラムに許可されますが、カーネルは2 47 ×(2 17 -1)〜264-1の仮想アドレス内に永続的に存在します。 (または、アドレスを符号付き整数として扱う場合は、-2 47から-1まで)。

64ビットWindowsで32ビット実行可能ファイルを実行するとどうなりますか?0から232(0-4GB)までのすべての仮想アドレスは簡単に利用できると思います、既存のプログラムのバグを公開しないように、32ビット実行可能ファイルはで再コンパイルしない限り0-2GBに制限され/LARGEADDRESSAWAREます。その場合は、0〜4GBにアクセスできます。(これは新しいフラグではありません。スイッチで実行されている32ビットWindowsカーネルにも同じことが適用さ/3GBれ、デフォルトの2G/2Gユーザー/カーネル分割が3G/1Gに変更されましたが、もちろん3〜4GBは範囲外です。 。)

どんな種類のバグがあるのでしょうか?例として、クイックソートを実装していて、2つのポインターがあり、配列の先頭と末尾を指しているとしますabのピボットとして中央を選択した場合(a+b)/2、両方のアドレスが2GB未満である限り機能しますが、両方が2GBを超えている場合、加算で整数のオーバーフローが発生し、結果は配列の外になります。(正しい式はa+(b-a)/2です。)

余談ですが、デフォルトの3G / 1Gユーザー/カーネル分割を使用する32ビットLinuxは、これまで2〜3 GBの範囲にあるスタックでプログラムを実行していたため、このようなプログラミングエラーはすぐに解消される可能性があります。64ビットLinuxは、32ビットプログラムに0〜4GBへのアクセスを提供します。

于 2009-07-12T01:33:40.480 に答える
3

Windows(他のOSと同様)は、カーネル+ドライバーよりもはるかに優れています。

アプリケーションは、カーネル空間に存在するだけではない多くのOSサービスに依存しています。プロセス自体のアドレス空間にマップできるバッファ、ハンドル、およびあらゆる種類のリソースがたくさんあります。たとえば、ウィンドウハンドルやブラシを返すWin32 API関数を呼び出すときは常に、それらをプロセスのどこかに割り当てる必要があります。したがって、Windowsの一部はカーネルで実行され、他の部分は独自のユーザーモードプロセスで実行され、アプリケーションが直接アクセスする必要のある部分は、アドレス空間にマップされます。これの一部は避けるのが難しいですが、重要な追加の要因はパフォーマンスです。すべての場合Win32呼び出しにはコンテキストスイッチが必要でした。これはパフォーマンスに大きな打撃を与えます。それらの一部が依存するデータがすでにアドレス空間にマップされているためにユーザーモードで処理できる場合、コンテキストスイッチは回避され、かなりのCPUサイクルを節約できます。

したがって、どのOSでも、ある程度のアドレス空間を確保する必要があります。LinuxはデフォルトでOSに1GBしか設定していないと思います。

MSがWindowsで2GBに落ち着いた理由は、RaymondChenのブログで一度説明されました。リンクがなく、詳細を思い出せませんが、Windows NTは元々Alphaプロセッサも対象としていたため、決定されました。Alphaの場合、50/50を実行する正当な理由がいくつかありました。スプリット。;)

これは、Alphaによる32ビットコードと64ビットコードのサポートと関係があります。:)

于 2009-07-12T01:34:59.730 に答える
2

カーネル モードで実行されるコード (つまり、デバイス ドライバー コード) には、独自のアドレス空間があります。

いいえ、違います。そのアドレス空間を x86 プロセッサのプロセスのユーザー モード部分と共有する必要があります。そのため、カーネルは合計で十分なスペースを確保し、アドレス空間を有限にする必要があります。

于 2009-07-12T01:21:05.723 に答える
1

最良の答えは、OS の設計者が、気にしなければならない頃には、人々は 64 ビット Windows を使用していると感じていたということだと思います。

しかし、ここにもっと良い議論があります。

于 2009-07-12T00:57:44.933 に答える
0

その答えの一部は、マイクロプロセッサ アーキテクチャの歴史に関係しています。ここに私が知っていることの一部を示しますが、他の人はより最近の詳細を提供できます。

Intel 8086 プロセッサは、20 ビットのメモリ アドレスを提供するメモリ用のセグメント オフセット アーキテクチャを備えていたため、アドレス指定可能な物理メモリの合計は 1MB でした。

当時の競合プロセッサ (Zilog Z80 など) とは異なり、Intel 8086 には、電子メモリだけでなく、キーボード、シリアル ポート、プリンタ ポート、ビデオ ディスプレイなどのマイナーな周辺機器とのすべての入出力通信に対応する必要のあるアドレス空間が 1 つしかありませんでした。 . (比較のために、Zilog Z80 には、アクセス専用のアセンブリ オペコードを備えた個別の入力/出力アドレス空間がありました)

増え続ける周辺機器の拡張に対応するためのスペースを確保する必要性から、アドレス空間を 0 ~ 640K の電子メモリに分割し、「その他のもの」 (入力/出力、ROM、ビデオ メモリなど) を 640K ~ 1MB。

x86 ラインが成長および進化し、PC がそれらと共に進化するにつれて、同様のスキームが使用され、今日の 4G アドレス空間の 2G/2G 分割で終わりました。

于 2009-07-12T01:18:53.590 に答える