リソース不足について - 第 2 回

こんにちは。 Windows テクノロジー サポートの田辺です。

リソース不足について - 第 2 回 では、ページ プールと非ページ プールについて もう少し掘り下げて見ていきたいと思います。

第 1 回と比較をすると細かい内容となりますが、トラブルシュートを行う上では欠かせない要素もいくつかありますので、がんばって進めていくことにしましょう。

割り当てられる領域につける名札について

 

ページ プールおよび非ページ プール内の領域が、各要求元のドライバに割り当て (Allocate) られる際には、Tag と呼ばれる 1 ~ 4 文字の名札がつけられます。

大抵この Tag には 要求元のドライバにより一意の文字が指定されるので、ドライバのどのような動作の際に確保されたプール (ページ プールもしくは 非ページ プール) なのかを判断する事ができ、この Tag 毎に使用されている値を見る事でプールの枯渇が発生した場合に、どのドライバが “限りあるリソース” を消費しているのかを追跡する事が可能となるのです。ただし、Tag そのものから確保されたプールがページ プールもしくは非ページ プールなのかを判別する事はできません。多くの場合、ページ プールと非ページ プールで共通の Tag が使用されます。

いくつか例を挙げてみますと、、、

MmSt - nt!mm - Mm section object prototype ptes

Ntf? - ntfs.sys - NTFS Specific allocation tags

Cc - nt!cc - Cache Manager allocations (catch-all)

といったものがあります。

これは OS が標準で使用しているものになりますが、各メーカーが開発しているドライバの中には、独自で指定している名前がいくつかあります。

余談ですがプールを確保する時は以下のようなパラメータを指定して Call されます。

PVOID ExAllocatePoolWithTag(

IN POOL_TYPE PoolType,

IN SIZE_T NumberOfBytes,

IN ULONG Tag <<<<<

);

Tag がこの名札にあたるわけですね。ULONG つまり 32-bit 値なので、慣例上 8-bit の ASCII コード 4 文字が使用されます。

また、PoolType でページ プールもしくは非ページ プールの種類を、NumberOfBytes で確保するページの大きさを指定します。

詳しくは以下の Windows Driver Kit の Reference (英語) をご参照ください。

// ExAllocatePoolWithTag

https://msdn.microsoft.com/en-us/library/ms796989.aspx

Tag ごとのプール使用量を確認する方法について

 

では、さっそく Tag ごとのプール使用量を確認する方法について見ていきましょう。

Debugger を使用して確認を行う方法等もありますが、今回は Poolmon というツールを利用する方法について紹介させていただきたいと思います。

Poolmon は、システムのページ プールと非ページ プールをプール割り当て Tag ごとにグループ化して表示させる事が可能な トラブルシュートには欠かせないツールです。

尚 Windows Server 2003 以降ではプールの Tag 付けは既定で有効になっているため、有効にする必要はありませんが、Windows Server 2003 よりも以前の OS では、プールの Tag 付けを有効にしてコンピュータを再起動する必要があります。

Windows Server 2003 よりも以前の OS で、プールの Tag 付けを有効にするには、gflags.exe を使用するか、レジストリを直接編集してグローバル フラグを設定する必要があります。

Poolmon の利用方法と併せて、プールの Tag 付けを有効にする方法については、技術情報 177415 でも紹介しておりますので、是非ご一読ください。

さて Windows Server 2003 の場合、poolmon は Windows Server 2003 の CD-ROM の \Support\Tools フォルダの中で人知れず眠っています。この保存場所は Windows 2000 と Windows XP の場合でも変わりません。

ここにある Support.CAB を展開すると、Poolmon.exe がいますので、目を覚ましてもらう事にしましょう。

(こう書くとちょっと怪しい技術書風になりますね)。

使用方法は至って簡単です。コマンド プロンプトの画面で、Support.CAB ファイルを展開したフォルダまで cd コマンドで移動して poolmon.exe と実行するだけです。

ちなみにコマンド プロンプトの画面は既定では小さなウインドウのため、[簡易編集モード] や [挿入モード] の有効化と、レイアウトの横と縦のバッファは十分な値を設定する事をお勧めします。この辺は好みに合わせてプロパティから変更してください。

私は コマンド プロンプトを使用する機会が多く、前に入力したコマンドが見えなくなってしまうのが困るので、縦が 9999、横が 200 くらいのバッファを指定していつも使ってます。こうすると全画面表示にも対応できる位のサイズになり、おかしな改行が入らないので非常に重宝します。

では さっそく Poolmon.exe を実行して、、、といきたいところですが、その前に第 1 回目の内容の復習も兼ねて実際に発生している問題のトラブルシュートを一緒に行っていきたいと思います。

まずは用意をしたテスト環境で前回ご紹介いたしました Memory\Pool NonPaged Bytes もしくは Memory\Pool Paged Bytes を見ていきます。

するとこの環境では、以下のように Pool Nonpaged Bytes が右肩上がりに上昇している事がわかりました。これだけで判断するには短期間過ぎではありますが、非ページプールが右肩上がりに上昇していて、リークが発生しているように見受けられます。

Perfmon

非ページ プールがリークしている可能性がある事がわかりましたので、ここからは実際に poolmon を使用して 非ページ プールを使用しているドライバを特定していきます。

コマンド プロンプト上で poolmon.exe と入力して実行すると、コマンド プロンプト上で以下のような画面が表示されたと思います。

poolmon1

この状態で 2 番目の列 "type" に "Nonp" という値が表示されるまで P キーを押します。

そして B キーを押すと、列の値が降順 (最大バイト利用数の順) で並べ替えられます。

poolmon2

簡易編集モードが有効の場合には、画面全体を選択してから右クリックをする事でコピーする事が出来ますので、そのまま notepad 等のテキスト エディタに張り付けます。この繰り返しで、そのタイミングの Tag 毎のプールの利用状況を残す事が出来ます。Poolmon.exe には、B の他にも出力を並べ替えるキーが 技術情報 177415 で紹介されてますので、目的に合わせてソートするようにしてください。

スナップショットはありませんが上の MeHE という Tag の使用量 (Bytes) が時間を追うごとに増えていることがわかります。ソートをしなおしても同じように増加をしていく Tag が無いため、全体量である Pool NonPaged Bytes を増加させていたのは、MeHE Tag である事がわかりました。

では 続いてこのドライバが何なのかを確認する事にしましょう。

Driver を特定する方法について

 

サードパーティ製のドライバによって使用されているプール Tag からドライバを特定する場合には、標準のコマンド “findstr” が大活躍します。

このコマンドを利用して各ドライバのバイナリ データから文字列を検索し、Tag 名と同じ文字列が含まれているドライバをある程度洗い出す事が出来るんですね。

サードパーティ製のドライバによって使用されているプール Tag の検索方法については、技術情報 298102 でも紹介しておりますので併せてご参照ください。

今回は MeHE という Tag で割り当てられた非ページ プールが時間を追う毎に増加しているような状況でしたので、MeHE という Tag を使っているドライバを特定したいと思います。

では早速実行してみましょう。

以下のコマンドを %systemroot%\system32\drivers 内のドライバに対して実行します。

> C:\Windows\System32\drivers>findstr /m /l MeHE *.sys

/m でファイルに一致する行があるときに、ファイル名のみを出力し、/l で検索文字列をリテラルとして使用、そして *.sys でドライバを指定しているといった塩梅です。検索を実行すると、実行後以下の結果が返されてきました。

> C:\Windows\System32\drivers>findstr /m /l MeHE *.sys

> Sheep.SYS

この結果からは、Sheep.SYS に MeHE という文字列が含まれている事がわかりましたので、Sheep.SYS が非ページ プールを割り当てる際に、MeHE という Tag を使用している可能性がある事がわかりました。

今回は意図的にリークが発生するように実装したドライバ (Sheep.sys) を使用してテストを行いましたので、結果としてドライバの特定まで出来て大成功という事になります。

// 補足

コマンドからだけでは無く、Tag を使用してエクスプローラからの検索も行う事が出来ますので是非お試しください。

ちなみにファイルの場所は drivers 配下だけではなく、%SystemRoot%、%ProgramFiles%、%SystemDrive%、%ProgramData% などのより広い範囲を対象として実行するなど、より包括的な検索を行わなければドライバを特定できない可能性があります。

drivers 配下に無い場合には、色々なフォルダ内を確認いただければと思います。

Driver が特定出来たらどうするか

 

Driver が特定出来たらどうするか ですが、自分で Driver を書いている方であればそのまま修正のコードを入れる事も出来ますが、簡単に修正を入れる事が出来ない方のほうが大多数かと思います。その場合には、もしリークが発生していた場合には、回避策としては再起動しかありません。

後はドライバの提供元にご相談をしていただき、解放漏れがある処理が無いかを確認してもらい、その部分が修正したモジュールを提供いただく他には無いので、リソース不足からドライバの特定ができた場合には、まずは提供元のメーカーにご相談いただくのが一番確実な対応策になります。

切り分け方法としては findstr 等で特定していただいたドライバを一時的に無効にして現象に変化が無いかを確認したり、更新された最新のドライバがあれば最新版のモジュールをインストールして確認を行ったりといろいろとありますが、まずは最新版のモジュールに更新していただくのが良いかと思います。

既に修正されている可能性があり、一番効率的なトラブルシュートなため、存在するかもわからない場合には提供元のメーカーにご相談ください。

注意事項

 

ちなみに一番多く使用しているから悪いのか?と言われるとそうでも無いのが困ったところでもあります。

うっかりだまされてしまうと、なかなか問題の解決に至らずに取り返しのつかない事にもなりかねません。

実はどのドライバがリークを引き起こしているのか、もしくは問題があるのかというのは、私たちも判断が非常に難しい時があります。

椅子取りゲームよろしく 共有されているリソースを取り合っているだけなので、実は確保している事に問題が無いとか、そのタイミングでは絶対に必要なので、むしろ他の部分でトリミングを行わなければいけないといったような事があるために、ただ一番多く消費しているからといって、問題ではない場合もありますのでご注意ください。

最後に

 

第 2 回まででページ プールと非ページ プールについてざっくりとではありますが、確保しているドライバの特定方法も含めて踏み込んだ解説をさせていただきました。

ボリュームが大きく わかりづらい内容ではありますが、詳細なトラブルシュートをする上では理解しておく必要がある部分ではありますので、実際に問題があった時などにここで紹介させていただいた事を思い出していただければと思います。

次回の第 3 回では、第 1 回で少し触れました ページ プールと非ページ プールの最大値を確認する方法についてと、Windows Vista と Windows Server 2008 でのページ プールと非ページ プールについてを紹介させていただこうかと思います。

Windows Vista 以降ではいろいろと変更があり、寂しい限り?ですがリソースの枯渇という事もあまり聞かなくなりました。そんな Windows Vista 以降の OS で、ページ プールと非ページ プールについてを中心に第 3 回は紹介させていただきますのでどうぞご期待ください。

 

 

~ 第 3 回 へつづく ~

 

- リソース不足について - 第 1 回

- リソース不足について - 第 2 回(今回)

- リソース不足について - 第 3 回