[WMI基礎] Remote で動作する仕組み + ExecQuery と ExecQueryAsync の違い ~ “wbemFlagForwardOnly” は Count プロパティが取れない ~

皆さんごきげんよう。ういこでございます。
今日は小ネタですが、意外に気になるその仕組み、ずばり「WMI をリモートで動作させる仕組みの基礎」「ExecQuery と ExecQueryAsync の違い」の二本をお送りします。久々な割に地味ネタですみません。

まず、大変大変ご無沙汰しておりましたことをお詫び申し上げます。IT 管理者の皆さまも、開発者の皆さまも、日本企業の会計年度末は目まぐるしい日々をお過ごしのことかと存じます。
私もちょっと、公私ともに忙しくてブログがご無沙汰になっておりました。すみません。

さて、リモートマシンー管理者の皆さまも、開発者の皆さまも等しく大好き WMI。すごい機能だけど重い、どのくらい重いかっていうと地球大気圏内のジ・○くらいでしょうか。あるいはサイコガン○ムくらいでしょうか。強力さもそれくらいですが。
しかし、どんなすごいモビルス○ツ機能でも、使い手がニュータイプじゃないとうまく動作しないのはファースト○ンダムのセ○ラさんの回を見た方はご理解いただけるかと存じます。というわけで、このブログで少しずつ小ネタを紹介させていただき、みんなで乗りこなしたれ!というのが理想ですね。…話がそれてすみません。本題に参ります。

(1) WMI をリモートで動作させる仕組みの基礎
コンピュータ "Hiroto" 上で動作するプログラムからリモートマシン "Shinichi" に対して WMI の要求をしてみたら、いったいどんな仕組みでどうさするんでしょう。
以前ご紹介させていただいたブログにて、WMI は RPC (Remote Procedure Call) を使うということに触れたことがあります。RPC はその名の通り、プロセス間でやりとりする仕組みです。ローカル コンピュータ上の自分のプログラムのプロセスの他のプロセスや、リモートのコンピュータ上で動作しているプロセスを呼び出すこともできます。WMI はこの RPC を使って処理を行っています。

おとなりの マイクロソフト Network & AD サポートチームさんがすごくわかりやすい(図つき)ブログを書いていらっしゃいますので、RPC ってなんだろう?という方はこちらをご覧いただけると良いかとおもいます。

RPC サーバーを利用できません? (By マイクロソフト Network & AD サポートチームさん)
https://blogs.technet.com/jpntsblog/archive/2009/09/02/rpc.aspx

では RPC をひとまず置いておいて、WMI をリモートで使うっていうことは、どんな動きになるのか?を見てみましょう。

(1) PC Hiroto 上のプログラムで、WMI の処理を実行します。
(2) RPC を通じ、リモートマシン Shinichi 上で WMI プロバイダ ホスト (Wmiprvse.exe) がもわっと起動されます。
(3) PC Shinichi 上の Wmiprvse.exe から、WQL で指定したクラスを実装しているファイルがロードされる。
(4) 実際の処理が (3) でロードされたモジュール内で実施される。
(5) クライアント Hiroto 上のプログラムは、リモートマシン Shinichi 上で処理された結果を受け取る。

image

要は、クライアント Hiroto 側のプログラムは実際の作業はリモートマシン Shinichi に処理をさせていて、その結果を取っているわけです。WMI のパフォーマンスのゆらぎに悩んだ経験のあるかたは、上記をみて「おや?」と思い当たる何かがあるかも…。うーん、見るからにリモートマシン側が忙しそうですよね。

さて、それでは次は、ふと考えたことがある方もいらっしゃるかと存じますが、"ExecQuery" と "ExecQueryAsync" の違いはなんでしょう?行ってみましょう。

(2) "ExecQuery" と "ExecQueryAsync" の違いはなんでしょう? ・ExecQuery メソッドの既定動作

ExecQuery は、同期処理と呼ばれる処理です。操作を実行し、何が起こるかを調べるために一時停止して、その後結果を返す、という動きになります。 上記の (1) – (5) は概念的なものです。上記の流れから、さらに細かいレベルの話になります。実際には、一度の流れですべての処理が終わるわけではありません。

1. クライアントにて、ExecQuery を実行します。 リモートマシンにクエリが送られます。
2. リモートマシンで、指定されたクエリを実行し、キャッシュを作成します。 クライアントには、一件目のデータを返します。
3. クライアントにて、一件目を受信し、処理します。 この間にも、リモートマシンでは検索処理を続行し、リモートマシン側キャッシュに追加します。
4. クライアントから次のデータの要求が行われます。 この時点では、リモートマシンではまだ検索処理を実行中である場合があります。
5. クライアントから次のデータの要求が行われます。
リモートマシンにて検索処理が終わっている場合、キャッシュから最後のデータを送信して、リモートマシン側の検索処理が終了します。

既定動作では、リモートマシン側はデータをキャッシュする処理と送信処理を同時に実行することになります。つまり、イベント ログなど、扱うレコード数が大量な場合は検索が完了まで、CPU 使用率が高負荷になる可能性がありえるということです。 概念図でさえあんなに忙しそうなのに、さらに忙しい感じがしませんか?実はたった一つにみえるクエリでも、実際にデータがすべてクライアントに流れてくるまでに、これだけのデータのやり取りやら、処理やらが行われているわけです。

CPU 使用率を抑えるためには "wbemFlagForwardOnly" フラグを使う
CPU 使用率のスパイクを回避するには…そう、負荷を下げればいいのです。では具体的に何をするか?それは、キャッシュと送信処理を同時にしなければいい、というのがひとつの考え方です。wbemFlagForwardOnly というフラグがこれを解決する手段となります。このフラグを指定すると、データキャッシュを生成させないことができるのです。このフラグは、ExecQuery の際に付加します。
キャッシュされないということは、その分のオーバーヘッドが減るので処理は高速化されることになります。
ただし、キャッシュされないということは、それなりにリスクもあります。

このフラグを指定すると、検索したオブジェクトについて、 Count プロパティが使用できないというリスクがあります。wbemFlagForwardOnly は、前方に進むことしか出来ないカーソルだからです。つまり n 件目を検索した後、 n+1 件目にカーソルをうつせますが、n+1 にカーソル移動した後に n に戻すことはできないということです。
Count プロパティは、検索完了後に初めて計算、値が入れられるプロパティであるため、この動作の影響を受けてしまいます。そのため、このフラグを指定した場合には参照できないということになるのです。

・ExecQueryAsync メソッドの場合
こちらは ExecQuery と対照的に、非同期動作になります。こちらを使用すると、リモートマシン側キャッシュの負荷が低くなります。
非同期メソッドはクエリを行うとすぐに復帰するため早いのですが、非同期メソッドの結果を受け取るためには、特別な SWbemSink というオブジェクトが必要になります。データの準備が出来次第、クライアント側に定義した Sink という関数が呼び出される動作になります。
プログラムでは、この関数にて、順次取得したレコードが処理されます。

データが大量になりがちな例 : イベントログ取得
非同期にしたり、wbemFlagForwardOnly にしたり、チューニングが必要になりがちなデータ大量になります系のシナリオとしては、イベントログがあります。
Win32_NTLogEvent クラスへの要求の場合は、起動されたリモートマシン側の wmiprvse.exe が、Win32_NTLogEvent クラスを実装している ntevt.dll をロードし、メッセージファイルに対する処理を行います。
実は、WMI 的にみると、イベント ログ エントリ自体は Win32_NTLogEvent という単一クラスのインスタンスの集合として扱われるため、ユーザから見て "System" やら "Security" "Application" やらといったログファイルごとに区別するような気が利いた扱いをしないのです。ということは、実は Where句で Logfile = 'System' のように、LogFile 属性を使用したとしても、その処理時間は、"全ての" イベントのレコード数に依存してしまうことになるのです…。

さらに微妙なことに、イベント ログのファイルは、インデックス化されているようなことはなく、ただのファイルであり、高速化するためにデータの整合的に並んでいるとかそういう気の利いたものでもないため、ファイルの読み書きをず~~~~っと上から進めていくしかないつくりになっています。そして、WMI のクエリのチューニングも限界があります。また、抽出条件の指定によって、データの大きさなどコンディションによってパフォーマンスに大きい違いが出ることもあります。

このように、WMI は便利ですが、動作基盤に結果がかなり左右されるうえ、パフォーマンスチューニングにどうしても限界があるテクノロジですので、ミッションクリティカルなシステムの場合は、WMI ではなくて、Win32 API など代替手段を使用されることをお勧めすることが多くあります。(以前の記事で紹介させていただいております ⇒ “EventLog を取得する方法 ~ とにかくイベントログを早くゲットしたい! ~ ” https://blogs.technet.com/jpilmblg/archive/2009/12/15/eventlog.aspx)
スクリプトホストで使用される場合は、インストールの必要がありますが、Log Parser ユーティリティを使うことができます。Log Parser は独自の検索ロジックを持っているのでマグネットコーティングを施した後のガ○ダムみたいに早いです。

参考 :
LIKE が好き
https://www.microsoft.com/japan/technet/scriptcenter/topics/win2003/like.mspx
Doctor Scripto のスクリプト ショップ
非同期イベントの監視によるペストウェアの制御
https://www.microsoft.com/japan/technet/scriptcenter/resources/scriptshop/shop0705.mspx
Example: Getting WMI Data from a Remote Computer
https://msdn2.microsoft.com/en-us/library/aa390422.aspx
文書番号: 154596 - 最終更新日: 2007年10月26日 - リビジョン: 13.3
ファイアウォールで動作するように RPC の動的ポート割り当てを構成する方法
https://support.microsoft.com/default.aspx?scid=kb;ja;154596

それでは、また~

ういこう@片頭痛の研究は超進んでるらしい。トリプタンって効くのかな?