Windowsでのプロセスとスレッドの列挙の高速化

Windowsで現在実行されているすべてのプロセスまたはスレッドをリストする必要がある場合があります。 これはさまざまな理由で必要になる場合があります。 おそらく、 Process Hackerのようなシステムユーティリティを作成しているのか、新しいプロセスまたはスレッドの開始/停止に何らかの形で応答したい(ログを作成し、チェックし、コードを挿入する)のかもしれません。 これを実装する最も正しい方法は、もちろん、ドライバーを作成することです。 ここではすべてがシンプルです-PsSetCreateProcessNotifyRoutinePsSetCreateThreadNotifyRoutineを使用して、プロセスとスレッドを開始/停止するときに呼び出されるコールバック関数を設定します。 非常に高速に動作し、リソースを消費しません。 それはすべての深刻なツールが行うことです。 しかし、ドライバーの開発が常に正しい方法とは限りません。 彼らは正しく書くことができる必要があり、最近(無料ではない)証明書で署名され、Microsoft(これは高速ではない)に登録される必要があります。 しかし、それらは配布するのに便利ではありません。たとえば、それらを含むプログラムをMicrosoft Storeにアップロードすることはできません。



それでは、パブリックWinAPIが提供するものを活用しましょう。 また、彼はCreateToolhelp32Snapshot()関数を提供しています。 この関数は、 このように使用することが提案されています。 すべてが良いようです-プロセス、フローに関する情報があります。 少しイライラするのは、エレガントなコールバックの代わりに、ループ内で無限のプーリングを行うことを余儀なくされるという事実ですが、それは問題ありません。



ここでの最大の問題はパフォーマンスです。 CreateToolhelp32Snapshot()+ Process32First()+ Process32Next()バンドルの動作は非常に遅いです。 おそらくこの問題は、Heap32First()+ Heap32Next()を使用して、 この記事で説明し問題と同じ領域にある可能性があります。 簡単に説明すると、歴史的な理由により、一部の場所では線形リストの通過に2次時間がかかります。



このすべてを何らかの形で加速することは可能ですか? できます。 ただし、パブリックWinAPI関数のみを使用するという明るい道を抜け出す必要があります。



長い間、WinAPI関数はパブリック(MSDNで説明されており、有効である)とドキュメント化されていない(MSDNで説明されておらず、公式にサポートされていないシステムライブラリのリバースエンジニアリング中に発見される)に分かれていると信じていました。 この白黒の世界は私にとってシンプルで論理的なもののように思えました。公開製品には、公開機能、個人トレーニング目的、ユーティリティ、内部ツールを使用します。文書化されていないものを使用してみてください。



しかし、これらの世界の間にはグレーゾーンがあることが判明しました。 これらはMSDNで説明されている関数です(これにより、それらは公開されているように見えます)が、Microsoftはいつでも(文書化されていないように)変更または削除できると述べています。 なぜそのような機能が存在するのですか? それは簡単です-一方では、それらの利点は隠せないほど大きいですが、他方では、OSの将来のバージョンはそれらを変更するための内部的な理由があるかもしれません。 このような機能は、私が遭遇した用語の1つで「プライベート」と呼ばれます。 例はNtQuerySystemInformation()です。



ドキュメントの最初の行を評価します-「NtQuerySystemInformationは、Windowsの将来のバージョンで変更されるか使用できなくなる可能性があります。 アプリケーションは、このトピックにリストされている代替機能を使用する必要があります。 そのような機能を使用することは可能ですか? 誰もが自分で決める。 個人的には、「狼の恐怖-森に行かないでください」と私には思えます。 これは、MSDNの他の機能が「Windowsの将来のバージョンで変更または使用不可」にならないことが保証されているかのようです。 OSの新しいバージョンでコードを確認する必要があります。 すべてのコードにサポートと適応が必要です。 そして、現在機能し、利点がある機能がある場合、それを使用しないのはなぜですか?



また、NtQuerySystemInformationには大きな利点があります。 CreateToolhelp32Snapshot()からNtQuerySystemInformation()に切り替えたときにMHookライブラリが受け取ったパフォーマンスの増加のグラフを次に示します。



画像



NtQuerySystemInformation()の使用方法は? 非常にシンプル:



  1. ライブラリ「ntdll.dll」で関数「NtQuerySystemInformation」を探しています。 理論的にはそこに見つからないかもしれませんが、実際にはVistaからWin10までのOSのすべてのバージョンで確かにそうです。 XPについてわからない(可能性がなく、確認する必要がなかった)
  2. 結果を使用してバッファにメモリを割り当てます
  3. SystemProcessInformationパラメーターを使用して関数を呼び出します
  4. 結果をバイパスし、現在のプロセスの記述構造から値「NextEntryOffset」を使用して個々のプロセスのレコード間を移動します。
  5. ステップ番号2で割り当てられたメモリを解放することを忘れないでください


コード例は、 ここまたはここにあります



個人的には、CreateToolhelp32Snapshot()からNtQuerySystemInformation()への移行を使用して、かなり要求の厳しい1つのシナリオで、合計プロセッサ負荷の約2%を獲得できました。



この話の教訓は、WinAPI関数の遅い動作が必ずしも最終的な文章ではないということです。 オペレーティングシステムは大きくて複雑なものであり、必要な情報を取得する別の方法になる可能性があります。



All Articles