Windows の限界に挑む: ページ プールと非ページ プール

翻訳元: Pushing the Limits of Windows: Paged and Nonpaged Pool (英語)

以前の「Windows の限界に挑む」の投稿では、物理メモリ仮想メモリという、2 つの主要な基本システム リソースについて説明しました。今回は、これらを基盤とする 2 つの基本的なカーネル リソースであるページ プールと非ページ プールについて説明します。ページ プールと非ページ プールは、最大プロセス数、同期オブジェクト、ハンドルなど、他の多くのシステム リソースの制限に直接関わっています。

ページ プールと非ページ プールには、オペレーティング システムとデバイス ドライバーがデータ構造の格納に使用するメモリ リソースとしての役割があります。プール マネージャーはカーネル モードで実行され、システムの仮想アドレス空間 (「Windows の限界に挑む」の仮想メモリに関する投稿で説明) の領域を使用して、メモリの割り当てを行います。カーネルのプール マネージャーは、ユーザー モード プロセス内で実行される C ランタイムや Windows ヒープ マネージャーと同様に動作します。最小限の仮想メモリ割り当てサイズは、システム ページ サイズ (x86 および x64 では 4 KB) の倍数になるため、これらの副次的なメモリ マネージャーによって大きな割り当ては小さく分割され、メモリが無駄になりません。

たとえば、アプリケーションがあるデータの格納に 512 バイトのバッファーを必要とする場合、ヒープ マネージャーが、割り当てられた領域の 1 つを取得し、最初の 512 バイトが使用中であることを記録します。このとき、このメモリへのポインターが返され、空きヒープ領域の追跡に使用されるリスト上に残りのメモリが配置されます。ヒープ マネージャーでは、割り当てられた 512 バイトの領域直後から始まる空き領域のメモリを使用して、それ以降の割り当てを行います。

非ページ プール

カーネルとデバイス ドライバーは非ページ プールを使用して、システムがページ フォールトを処理できないときにアクセスされる可能性があるデータを格納しています。カーネルがこのような状態になるのは、割り込みサービス ルーチン (ISR) と 遅延プロシージャ呼び出し (DPC) を実行したときです。両者はハードウェアの割り込みに関連する機能です。ページ フォールトも、カーネルまたはデバイス ドライバーがスピン ロックを取得したときに発生する無効な状態です。スピン ロックは、ISR 内および DPC 内で使用できる唯一の種類のロックであり、ISR または DPC 内から、他の ISR や DPC から、あるいはカーネル スレッドで実行されるコードからアクセスされるデータ構造の保護に使用する必要があります。これらのルールに従おうとしてドライバーでエラーが発生すると、最も一般的なクラッシュ コード IRQL_NOT_LESS_OR_EQUAL (英語) が発生します。

したがって、非ページ プールは常に物理メモリ上に存在し続け、非ページ プールの仮想メモリには物理メモリが割り当てられます。非ページ プールに格納される一般的なシステム データ構造には、プロセスやスレッドを表すカーネルとオブジェクト、ミューテックスなどの同期オブジェクト、セマフォとイベント、ファイル オブジェクトとして表されるファイルへの参照、および I/O 処理を表す入出力要求パケット (IRP) などがあります。

ページ プール

一方のページ プールは、Windows が格納しているデータをページング ファイルに書き込めるというのがその名の由来で、使用中の物理メモリを別の目的に使用することができます。ユーザー モードの仮想メモリと同様、ページング ファイル内にあるページ プール メモリをドライバーまたはシステムが参照すると、ページ フォールトを呼び出す処理が発生し、メモリ マネージャーが物理メモリにデータを読み直します。ページ プールを最も消費するのは、少なくとも Windows Vista 以降については、通常はレジストリです。レジストリ キーやその他のレジストリ データ構造の参照はページ プールに格納されます。メモリ マップ ファイルを表すデータ構造は、内部的に "セクション" と呼ばれ、やはりページ プールに格納されます。

デバイス ドライバーは ExAllocatePoolWithTag (英語) API を使用して、パラメーターの 1 つとして必要なプールの種類を指定し、非ページ プールおよびページ プールを割り当てます。もう 1 つのパラメーターは 4 バイトの "タグ" で、ドライバーはこのパラメーターを使用して割り当てたメモリを一意に特定することができます。また、後で説明しますが、プールをリークするドライバーを追跡するためのキーとしても便利に使用できます。

ページ プールと非ページ プールの使用状況の表示

プールの使用状況を示すパフォーマンス カウンターは 3 つあります。

  • Pool nonpaged bytes
  • Pool paged bytes (ページ プールの仮想サイズ - 一部はページ アウトされている場合があります)
  • Pool paged resident bytes (ページ プールの物理サイズ)

ただし、これらのプールの最大サイズに対するパフォーマンス カウンターはありません。最大サイズはカーネル デバッガーの !vm コマンドで表示できますが、Windows Vista 以降でカーネル デバッガーをローカル カーネル デバッグ モードで使用するには、システムをデバッグ モードでブートする必要があり、MPEG2 の再生ができなくなります。

そこで、代わりに Process Explorer を使用すると、現在割り当てられているプール サイズと最大サイズの両方を表示できます。最大サイズを表示するには、オペレーティング システムのシンボル ファイルを使用するように Process Explorer を構成する必要があります。まず、最新の Windows 用デバッグ ツール パッケージをインストールします。その後、Process Explorer を実行し、[Options] メニューの [Symbol Configuration] ダイアログ ボックスを開きます。次に、Windows 用デバッグ ツールをインストールしたディレクトリにある dbghelp.dll を指定し、シンボル パスにマイクロソフトのシンボル サーバーを設定します。

図 1

図 1

シンボルを構成したら、[System Information] ダイアログ ボックスを開く ([View] メニューの [System Information] をクリック、または Ctrl + I キーを押す) と、[Kernel Memory] セクションにプール情報が表示されます次に示すのは、2 GB の Windows XP システムでの表示例です。

図 2

図 2  2 GB 32 ビット Windows XP

非ページ プールの制限

前回の投稿で説明したように、32 ビット版 Windows では、システムの既定のアドレス空間は 2 GB です。これは、本質的に非ページ プールに (またはあらゆるシステム仮想メモリに) 2 GB という上限を課すものです。ただし、非ページ プールは、他の種類のリソース (カーネル自身、デバイス ドライバー、システムのページ テーブル エントリ (PTE)、キャッシュされたファイルのビューなど) と、空間を共有しなければなりません。

Vista より前の 32 ビット版 Windows では、ブート時に各種要素に割り当てるアドレス空間が計算されます。その数式にはさまざまな要素が組み込まれますが、最も大きな要素はシステムの物理メモリの量です。非ページ プールに割り当てられる量は、512 MB のシステムでの 128 MB から始まり、1 GB 強を超えるシステムでは最大 256 MB になります。/3GB オプションでブートしたシステムでは、カーネルのアドレス空間を使ってユーザー モードのアドレス空間が 3 GB に拡張され、最大の非ページ プールは 128 MB になります。上の Process Explorer のスクリーンショットでは、/3GB スイッチを指定せずにブートした 2 GB の Windows XP システムで最大 256 MB と報告されています。

Server 2008 や Windows 7 を含む 32 ビット版 Windows Vista 以降 (ただし、32 ビット版の Windows Server 2008 R2 は存在しません) では、システム アドレスは静的に区分されておらず、変化する需要に合わせてさまざまな種類のメモリに範囲を動的に割り当てます。ただし、非ページ プールには物理メモリの量に基づく最大値が設定されており、物理メモリの 75% を若干上回る値か 2 GB のうち、小さい方になります。次に示すのは、2 GB の Windows Server 2008 システムでの最大値です。

図 3

図 3 2 GB 32 ビット Windows Server 2008

64 ビット版 Windows システムのアドレス空間はそれよりはるかに大きく、メモリ マネージャーがアドレス空間を静的に区分しても、空間が不足するおそれはありません。64 ビット版の Windows XP および Windows Server 2003 では、RAM 1 MB あたり 400 KB か 128 GB のうち、小さい方になります。次に示すのは、2 GB の 64 ビット版 Windows XP システムのスクリーンショットです。

図 4

図 4 2 GB 64 ビット Windows XP

64 ビット版の Windows Vista、Windows Server 2008、Windows 7、および Windows Server 2008 R2 のメモリ マネージャーは、32 ビットのメモリ マネージャー (該当する場合 - 前に述べたように、32 ビット版の Windows Server 2008 R2 は存在しません) と同様に、最大値を RAM の約 75% に設定しますが、上限は 2 GB ではなく 128 GB に制限されています。次に示すのは、2 GB の 64 ビット版 Windows Vista システムのスクリーンショットです。前に示した 32 ビット版 Windows Server 2008 システムと同様の非ページ プールの制限があります。

図 5

図 5 2 GB 32 ビット Windows Server 2008

最後に、8 GB の 64 ビット版 Windows 7 システムでの制限を示します。

図 6

図 6 8 GB 64 ビット Windows 7

次に示すのは、Windows の各種バージョン間の非ページ プールの制限をまとめた表です。

  32 ビット 64 ビット
XP、Server 2003 1.2 GB 以下の RAM: 32 ~ 256 MB
1.2 GB 超の RAM: 256 MB
RAM 1 MB あたり 400 KB または 128 GB のうち小さい方
Vista、Server 2008、
Windows 7、Server 2008 R2
RAM の 75% または 2 GB のうち小さい方 RAM の 75% または 128 GB のうち小さい方

ページ プールの制限

カーネルとデバイス ドライバーはページ プールを使用して、DPC や ISR 内部からアクセスできないデータ構造、またはスピンロック時にアクセスできないデータ構造を格納します。これは、ページ プールのコンテンツが、物理メモリ内に存在することもでき、また、メモリ マネージャーのアルゴリズムによって物理メモリを別の目的に利用することが決定した場合にページング ファイルに送信しておき、再度参照して問題が生じた際に物理メモリに戻すこともできるためです。そのため、ページ プールの制限は、主にメモリ マネージャーがページ プールに割り当てたシステム アドレス空間の量、およびシステムのコミット制限によって決まります。

32 ビット版 Windows XP では、他のリソース (システムの PTE が最も代表的です) に割り当てられているアドレス空間の量によって制限が計算され、上限は 491 MB です。たとえば、前に示した 2 GB の Windows XP システムでは、360 MB の制限があります。

図 7

図 7 2 GB 32 ビット Windows XP

32 ビット版 Windows Server 2003 では、ページ プールにより多くの空間が予約され、上限は 650 MB です。

32 ビット版の Windows Vista 以降では、カーネルのアドレス空間を動的に利用できるので、制限は単に 2 GB に設定されています。そのため、システムのアドレス空間がいっぱいになるか、システムのコミット制限に達すると、ページ プールが不足します。

64 ビット版の Windows XP および Windows Server 2003 では、最大値が非ページ プールの制限の 4 倍か 128 GB のうち、小さい方に設定されます。もう一度 64 ビット版 Windows XP システムのスクリーンショットを示します。ページ プールの制限は、正確に非ページ プールの 4 倍になっています。

図 8

図 8 2 GB 64 ビット Windows XP

最後に 64 ビット版の Windows Vista、Windows Server 2008、Windows 7、および Windows Server 2008 R2 の場合ですが、最大値は単に 128 GB に設定されています。ページ プールの制限でシステムのコミット制限を追跡することができます。64 ビット版 Windows 7 システムのスクリーンショットをもう一度示します。

図 9

図 9 8 GB 64 ビット Windows 7

ここで、オペレーティング システム間のページ プールの制限をまとめます。

  32 ビット 64 ビット
XP、Server 2003 XP: 491 MB 以下
Server 2003: 650 MB 以下
非ページ プール制限の 4 倍、または 128 GB のうち小さい方
Vista、Server 2008、
Windows 7、Server 2008 R2
システムのコミット制限、または 2 GB のうち小さい方 システムのコミット制限、または 128 GB のうち小さい方

プールの制限のテスト

カーネル プールはほとんどすべてのカーネル処理で使用されるため、プールを使い果たすと予測できない結果が生じる場合があります。プールが少なくなったときにシステムがどのように動作するかを直接確認するには、Notmyfault (英語) ツールを使用します。このツールには、指定した増分値で非ページ プールまたはページ プールのいずれかにリークを発生させるオプションがあります。リーク率を変更する必要がある場合、リーク中にリーク サイズを変更できます。また、Notmyfault は、終了時にリークしたメモリをすべて解放します。

図 10

図 10

データ損失対策を講じていない場合、このツールをシステムで実行しないでください。プールが不足すると、アプリケーションや I/O 処理が停止するため、データ損失の可能性があります。ドライバーがメモリ不足の状態を正しく処理できない場合 (ドライバーのバグと考えられます)、ブルー スクリーンが表示される可能性さえあります。Windows Hardware Quality Laboratory (WHQL) では、Driver Verifier の使用を強く推奨しています。このツールは Windows に組み込まれていて、プールが不足した状態でクラッシュせずにドライバーを使用できるかどうかを確認できます。しかしながら、このようなテストを行っていないか、または WHQL のテスト中に見つからなかったバグを含むサード パーティのドライバーが存在する可能性はあります。

Notmyfault を仮想マシンのさまざまなテスト システムで実行して、どのような動作になるかを確認したところ、システム クラッシュは発生しませんでしたが、異常な動作が確認されました。64 ビット版 Windows XP システムで非ページ プールが不足すると、たとえば、コマンド プロンプトを起動しようとした場合に次のようなダイアログ ボックスが表示されます。

図 11

図 11

32 ビット版 Windows Server 2008 システムでは既にコマンド プロンプトを実行中でしたが、カレント ディレクトリを変更したりディレクトリの内容を表示するような単純な処理さえ、非ページ プールを使い果たした後では失敗しました。

図 12

図 12

あるテスト システムでは、最終的に、データが失われる可能性があることを示すエラー メッセージが表示されました。みなさんの運用システムで、このようなダイアログ ボックスが表示されないことを切に願います。

図 13

図 13

ページ プールが不足しても、同様のエラーが発生します。次に示すのは、32 ビット版 Windows XP システムで、ページ プールが不足した後でコマンド プロンプトからメモ帳を起動しようとした結果です。Windows によるウィンドウのタイトル バーの再描画が失敗し、何回かメモ帳を起動しようとして異なるエラーが表示されていることに注目してください。

図 14

図 14

次に示すのは、ページ プールが不足している 64 ビット版 Windows Server 2008 システムで [スタート] メニューの [アクセサリ] フォルダーの内容を表示できなかった例です。

図 15

図 15

システム コミットのレベルを次に示します。これは、Process Explorer の [System Information] ダイアログ ボックスにも表示され、Notmyfault がページ プールの大きなチャンクをリークするとすぐに上昇し、2 GB の 32 ビット版 Windows Server 2008 システムの最大値である 2 GB に達しています。

図 16

図 16

プールを使い果たし、システムが使えない状態であっても Windows が単純にクラッシュしないのは、プールが使い果たされたのは極端なワーク ロードのピークによって生じた一時的な状態で、その後プールが解放され、システムが正常に戻るという可能性があるためです。ただし、ドライバー (またはカーネル) がプールをリークしている場合、永続的にこのような状態になるため、リークの原因を特定することが重要になります。この投稿の冒頭に記したプール タグの役割はそこにあります。

プール リークの追跡

プールのリークが疑われ、システムがまだ他のアプリケーションを起動できる場合は、Windows Driver Kit に含まれる Poolmon というツールを使用すると、プール種別ごとの割り当て数やバイト数が突出している割り当て、および ExAllocatePoolWithTag の呼び出しに渡されたタグが表示されます。Poolmon では、さまざまなホット キーで各列を並べ替えることができます。リークが生じている割り当ての種類を見つけるには、"b" を押してバイト数で並べ替えるか、"d" を押して割り当て数と空き容量との差異で並べ替えます。次に示すのは、Notmyfault により、14 の割り当てでそれぞれ約 100 MB のリークを発生させたシステムでの Poolmon の表示です。

図 17

図 17

左の列で疑わしいタグを特定したら (この場合 "Leak")、次に、そのタグを使用しているドライバーを見つけます。タグはドライバーのイメージに保存されているため、ドライバー イメージをスキャンすれば問題のタグを見つけることができます。Sysinternals の Strings (英語) ユーティリティを使用すると、指定したファイル内で表示可能な文字列がダンプされます (既定では、3 文字以上の長さの文字列になります)。また、ほとんどのデバイス ドライバーのイメージは、%Systemroot%\System32\Drivers ディレクトリにあるため、コマンド プロンプトを開き、このディレクトリに移動して、「strings * | findstr <tag>」を実行することができます。一致する箇所が見つかったら、Sysinternals の Sigcheck (英語) ユーティリティで、ドライバーのバージョン情報をダンプできます。次に示すのは、"Leak" を使用しているドライバーを検索したときのプロセスの様子です。

図 18

図 18

システムがクラッシュし、プールを使い果たしたことが原因と疑われる場合は、Windbg デバッガーにクラッシュ ダンプ ファイルを読み込みます。このデバッガーは、Windows 用デバッグ ツール パッケージに含まれており、!vm コマンドを使用すると確認できます。次に示すのは、Notmyfault が非ページ プールを使い果たしたシステムでの !vm の出力です。

図 19

図 19

リークを確認したら、!poolused コマンドを使用すると、タグごとのプールの使用状況が Poolmon と同様のビューで表示されます。既定では、!poolused は概要を並べ替えられていない状態で表示します。オプションとして 1 を指定するとページ プールの使用状況で並べ替えられ、2 を指定すると非ページ プールの使用状況で並べ替えられます。

図 20

図 20

ダンプの発生元であるシステム上で Strings を使用して、問題の原因となっているタグを使用しているドライバーを検索します。

このブログではこれまで、物理メモリ、仮想メモリ、ページ プールと非ページ プールといった、Windows の最も基本的な制限について取り上げました。次回は、これらの制限から派生する、Windows がサポートするプロセスとスレッドの数の制限について取り上げます。

公開: 2009 年 3 月 26 日木曜日 4:02 PM 投稿者: markrussinovich (英語)

ページのトップへ

共有

ブログにコピー: ([Ctrl] + [C] でコピーしてください)