トップ回答者
DISPATCH_LEVELとDPC

質問
-
基本的なことだと思うのですが、はっきりとわからなかったので、質問させて下さい。
あるスレッドがDISPATCH_LEVELでIRPを送ってきたとします。
IRPを処理中に、何らかのデバイスの割り込みが発生し(自分のデバイスでなくてもいい)、DIRQLに引き上げられて割り込みルーチンを処理します。
その後、割り込みルーチンが処理を終えたときに、他にDIRQLで待機中のデバイスが無いとすると、再びDISPATCH_LEVELに戻ると思います。
この時、DPCは実行されるのでしょうか?現在開発しているドライバで、IRPを処理しているときに上記のようなタイミングでDPCによって別のデバイスのCompleteRequestが発生し、まわりまわって自分のデバイスにIRPが発行されて再入しているのではないかという事を懸念しています。
また、DPCを実行している間、スレッドは何になるのでしょうか?割り込まれる直前のスレッドか、DPCを専用に実行するスレッドか、不特定でしょうか?
- 移動 Mike Wang (MSCS) 2012年10月2日 12:43 (移動元:Windows デバイスドライバー開発)
回答
-
いままでそこまで深く掘り下げて考えてみたことはありませんけど、確かに言われてみれば気になるところですね。
残念ながら、私にはこの質問に対して明確な返信をできるほどのスキルは無いのですが、以下は一応私なりの見解です。
(参考になるかどうかわかりませんけど。。。)ご質問の趣旨は、
「DISPATCH_LEVEL で処理中の IRP が、ISR などから生成された DPC に割り込まれることはあるのか?」
と言うことだと思うのですが、個人的にはそれは「ない」と考えています。
(もちろん、ドライバの作り方にも依存すると思いますが。。。)
その理由としては、DPC も DISPATCH_LEVEL で動作するからです。
もっと厳密に言うと、DPC とシステムのスレッド スケジューラも DISPATCH_LEVEL で動作するからです。
この動作に関しては、下記サイトから正式な「日本語」ドキュメントも公開されています。----------------------------------------
スケジューリング、スレッド コンテキスト、および IRQLhttp://www.microsoft.com/japan/whdc/driver/kernel/IRQL.mspx
----------------------------------------仮に、現在 DISPATCH_LEVEL で処理中の IRP が DIRQL からの割り込みで中断され、その ISR (Interrupt Service Routine) が
残りの処理のために DPC オブジェクト (DPC ルーチン) をキューイングさせたとしても、その DPC ルーチンを呼び出すためには
スレッド コンテキストの切り替えが必要になるはずだと思います。
スレッド コンテキストの切り替えは、スレッド スケジューラによりスケジュールされた順番に実行されると思いますが、
DPC もスレッド スケジューラも、いずれも DISPATCH_LEVEL で実行されるので、DIRQL から再び DISPATCH_LEVEL に戻されたときに、
そのタイミングで DISPATCH_LEVEL で処理するようスケジュールされているスレッドは、DIRQL で割り込まれた DISPATCH_LEVEL で
IRP を処理しているスレッドを実行する直前にスケジュールされたスレッドのはずだと思います。
(↑ わかりにくい日本語ですみません、国語は苦手なので。。。ちなみに英語は苦手以前で、まったくわかりませんけど。)
要するに、現在 DISPATCH_LEVEL で処理中の IRP が完了する (あるいは APC_LEVEL 以下に引き下げられる) までは
スレッド スケジューラは動けないないので、DIRQL での ISR からキューイングされた DPC オブジェクトが実行されるのは、
次にスレッド スケジューラがスケジュールした後になるはず。。。と言うことです。もっとも、DISPATCH_LEVEL で IRP の処理を開始する直前で DPC が起動された場合には、kekyo さんがご指摘されている状況に
なる可能性はあると思いますが、もしそのような状況が発生するのであれば、それ以前にドライバの仕様設計に問題があるような
気がします。
あと、> また、DPCを実行している間、スレッドは何になるのでしょうか?割り込まれる直前のスレッドか、DPCを専用に実行するスレッドか、不特定でしょうか?
に関しては、その質問の趣旨がよく理解出来ていないので頓珍漢なお返事かもしれませんが、
「DPC オブジェクト用に生成されたシステム スレッド」
と言うことではないのかと。。。。
(質問の趣旨を間違えていたらすみません。。。)
ちなみに。。。。> あるスレッドが DISPATCH_LEVEL で IRP を送ってきたとします。
↑ これって、このリクエストを発行してきた側のマナー違反ですよね?
以下のサイトにも記載されていますけど、基本的には Dispatch Routine へのコールは、
(一部例外があるにしても) IRQL = PASSIVE_LEVEL で来るべきだと思っています。----------------------------------------
Dispatch Routines and IRQLshttp://msdn.microsoft.com/en-us/library/ff544039(VS.85).aspx
----------------------------------------ただ、WDK サンプルの DiskPerf とかの Disk Filter を Vista とかに組み込むと、
rdyboost とかから IRQL = DISPATCH_LEVEL で来る場合があり(。。。確かあったような気がする。。。)、
「これってありなの?」
って感じています。もっとも rdyboost に限らず、自分の上位にこのような処理を行うドライバ (例えば Completion Routine から
その Context で別途新規の IRP を発行するようなドライバ) がいた場合には同様なことが発生するはずですし、
実際 Driver Verifier (verifier.exe) でテストすると IRQL = DISPATCH_LEVEL で Dispatch Routine に
来る場合があるので、
「自分ではそーいう実装はしちゃいけないけど、他人がそーする場合があるので、ちゃんと考慮しなさい!!」
ってことなんでしょうね、きっと。。。- 回答としてマーク kekyoMVP 2010年10月18日 8:38
すべての返信
-
いままでそこまで深く掘り下げて考えてみたことはありませんけど、確かに言われてみれば気になるところですね。
残念ながら、私にはこの質問に対して明確な返信をできるほどのスキルは無いのですが、以下は一応私なりの見解です。
(参考になるかどうかわかりませんけど。。。)ご質問の趣旨は、
「DISPATCH_LEVEL で処理中の IRP が、ISR などから生成された DPC に割り込まれることはあるのか?」
と言うことだと思うのですが、個人的にはそれは「ない」と考えています。
(もちろん、ドライバの作り方にも依存すると思いますが。。。)
その理由としては、DPC も DISPATCH_LEVEL で動作するからです。
もっと厳密に言うと、DPC とシステムのスレッド スケジューラも DISPATCH_LEVEL で動作するからです。
この動作に関しては、下記サイトから正式な「日本語」ドキュメントも公開されています。----------------------------------------
スケジューリング、スレッド コンテキスト、および IRQLhttp://www.microsoft.com/japan/whdc/driver/kernel/IRQL.mspx
----------------------------------------仮に、現在 DISPATCH_LEVEL で処理中の IRP が DIRQL からの割り込みで中断され、その ISR (Interrupt Service Routine) が
残りの処理のために DPC オブジェクト (DPC ルーチン) をキューイングさせたとしても、その DPC ルーチンを呼び出すためには
スレッド コンテキストの切り替えが必要になるはずだと思います。
スレッド コンテキストの切り替えは、スレッド スケジューラによりスケジュールされた順番に実行されると思いますが、
DPC もスレッド スケジューラも、いずれも DISPATCH_LEVEL で実行されるので、DIRQL から再び DISPATCH_LEVEL に戻されたときに、
そのタイミングで DISPATCH_LEVEL で処理するようスケジュールされているスレッドは、DIRQL で割り込まれた DISPATCH_LEVEL で
IRP を処理しているスレッドを実行する直前にスケジュールされたスレッドのはずだと思います。
(↑ わかりにくい日本語ですみません、国語は苦手なので。。。ちなみに英語は苦手以前で、まったくわかりませんけど。)
要するに、現在 DISPATCH_LEVEL で処理中の IRP が完了する (あるいは APC_LEVEL 以下に引き下げられる) までは
スレッド スケジューラは動けないないので、DIRQL での ISR からキューイングされた DPC オブジェクトが実行されるのは、
次にスレッド スケジューラがスケジュールした後になるはず。。。と言うことです。もっとも、DISPATCH_LEVEL で IRP の処理を開始する直前で DPC が起動された場合には、kekyo さんがご指摘されている状況に
なる可能性はあると思いますが、もしそのような状況が発生するのであれば、それ以前にドライバの仕様設計に問題があるような
気がします。
あと、> また、DPCを実行している間、スレッドは何になるのでしょうか?割り込まれる直前のスレッドか、DPCを専用に実行するスレッドか、不特定でしょうか?
に関しては、その質問の趣旨がよく理解出来ていないので頓珍漢なお返事かもしれませんが、
「DPC オブジェクト用に生成されたシステム スレッド」
と言うことではないのかと。。。。
(質問の趣旨を間違えていたらすみません。。。)
ちなみに。。。。> あるスレッドが DISPATCH_LEVEL で IRP を送ってきたとします。
↑ これって、このリクエストを発行してきた側のマナー違反ですよね?
以下のサイトにも記載されていますけど、基本的には Dispatch Routine へのコールは、
(一部例外があるにしても) IRQL = PASSIVE_LEVEL で来るべきだと思っています。----------------------------------------
Dispatch Routines and IRQLshttp://msdn.microsoft.com/en-us/library/ff544039(VS.85).aspx
----------------------------------------ただ、WDK サンプルの DiskPerf とかの Disk Filter を Vista とかに組み込むと、
rdyboost とかから IRQL = DISPATCH_LEVEL で来る場合があり(。。。確かあったような気がする。。。)、
「これってありなの?」
って感じています。もっとも rdyboost に限らず、自分の上位にこのような処理を行うドライバ (例えば Completion Routine から
その Context で別途新規の IRP を発行するようなドライバ) がいた場合には同様なことが発生するはずですし、
実際 Driver Verifier (verifier.exe) でテストすると IRQL = DISPATCH_LEVEL で Dispatch Routine に
来る場合があるので、
「自分ではそーいう実装はしちゃいけないけど、他人がそーする場合があるので、ちゃんと考慮しなさい!!」
ってことなんでしょうね、きっと。。。- 回答としてマーク kekyoMVP 2010年10月18日 8:38
-
回答ありがとうございます。
「スケジューリング、スレッド コンテキスト、および IRQL」の題目のドキュメントは、投稿後にいろいろ検索している最中に発見して読みました。このドキュメント上でも明確にDPCの構造を述べているわけではありませんが、「カーネルは、プロセッサごとに DPC のキューを管理し、プロセッサの IRQL が DISPATCH_LEVEL より低くなる直前にこのキューから DPC を実行します。」とあるので、間違いないような気がします。この「DISPATCH_LEVELより低くなる直前」という所が知りたかったところで、「DISPATCH_LEVELになるときにDPCが発行される」と書いてある資料が多いため、ずっと自分の中でくすぶり続けていました(日本語訳だからでしょうか、英語本では誤解無いように書かれているのかもしれません)。
つまり、DPCが処理されるタイミングは、DISPATCH_LEVEL→APC_LEVEL(またはPASSIVE_LEVEL)のトリガーの際に、DPCキューにアイテムがあれば処理される、ということで、ちょっと自信が持てました。また、思考シミュレーションもしたのですが、一般的に同期処理に使うスピンロックはDISPATCH_LEVELに引き上げますが、これでDPCに割り込まれたら、スピンロックは同期制御として殆ど役に立たないだろうという結論でした。
> あるスレッドが DISPATCH_LEVEL で IRP を送ってきたとします。
経緯をあまり詳しく書いて無くてすいません。ディスクデバイスのフィルタドライバを書いています。デバイスがページファイルの格納先に選択されるときには、DISPATCH_LEVELでIRPを送ってきます。これは資料で知っていたのですが、
> 実際 Driver Verifier (verifier.exe) でテストすると IRQL = DISPATCH_LEVEL で Dispatch Routine に 来る場合があるので、
今のドライバはDISPATCH_LEVELで来ることを想定しているのですが、こういうことがあるんですね。意識していなかった (^^;
とても参考になりました。ありがとうございます。
-
この「DISPATCH_LEVELより低くなる直前」という所が知りたかったところで、「DISPATCH_LEVELになるときにDPCが発行される」と書いてある資料が多いため、ずっと自分の中でくすぶり続けていました
すでに解決済みの質問であることは承知しておりますが、上の引用情報の理解に有益ではないかと勝手に判断し、次の情報を投稿させていただきます。私自身はドライバーの開発を行っておりませんし、また、今後も行う予定はございません。投稿情報がご迷惑にならないことを願うばかりです。
System(0xfffffa8003c8e950) has 124 active threads
THREAD fffffa8003ccd370 Cid 0004.0068 Teb: 0000000000000000 Win32Thread: 0000000000000000 WAIT: (Suspended) KernelMode Non-Alertable
fffff8000178bb00 SynchronizationEvent
Not impersonating
DeviceMap fffff88000007400
Owning Process fffffa8003c8e950 Image: System
Attached Process N/A Image: N/A
Wait Start TickCount 867768 Ticks: 652 (0:00:00:10.171)
Context Switch Count 905
UserTime 00:00:00.000
KernelTime 00:00:00.000
Win32 Start Address nt!KiExecuteDpc (0xfffff8000163c2e4)
Stack Init fffffa6001b16db0 Current fffffa6001b16a50
Base fffffa6001b17000 Limit fffffa6001b11000 Call 0
Priority 31 BasePriority 8 PriorityDecrement 0 IoPriority 2 PagePriority 5
Child-SP RetAddr Call Site
fffffa60`01b16a90 fffff800`0166ef8a nt!KiSwapContext+0x7f
fffffa60`01b16bd0 fffff800`0167038a nt!KiSwapThread+0x2fa
fffffa60`01b16c40 fffff800`0163c48f nt!KeWaitForSingleObject+0x2da
fffffa60`01b16cd0 fffff800`0188cde3 nt!KiExecuteDpc+0x1ab
fffffa60`01b16d50 fffff800`016a3536 nt!PspSystemThreadStartup+0x57
fffffa60`01b16d80 00000000`00000000 nt!KiStartSystemThread+0x16
System(0xfffffa8003c8e950) has 124 active threads
THREAD fffffa8003cce040 Cid 0004.006c Teb: 0000000000000000 Win32Thread: 0000000000000000 WAIT: (Suspended) KernelMode Non-Alertable
fffffa60005ef600 SynchronizationEvent
Not impersonating
DeviceMap fffff88000007400
Owning Process fffffa8003c8e950 Image: System
Attached Process N/A Image: N/A
Wait Start TickCount 867778 Ticks: 642 (0:00:00:10.015)
Context Switch Count 904
UserTime 00:00:00.000
KernelTime 00:00:00.000
Win32 Start Address nt!KiExecuteDpc (0xfffff8000163c2e4)
Stack Init fffffa6001b1ddb0 Current fffffa6001b1da50
Base fffffa6001b1e000 Limit fffffa6001b18000 Call 0
Priority 31 BasePriority 8 PriorityDecrement 0 IoPriority 2 PagePriority 5
Child-SP RetAddr Call Site
fffffa60`01b1da90 fffff800`0166ef8a nt!KiSwapContext+0x7f
fffffa60`01b1dbd0 fffff800`0167038a nt!KiSwapThread+0x2fa
fffffa60`01b1dc40 fffff800`0163c48f nt!KeWaitForSingleObject+0x2da
fffffa60`01b1dcd0 fffff800`0188cde3 nt!KiExecuteDpc+0x1ab
fffffa60`01b1dd50 fffff800`016a3536 nt!PspSystemThreadStartup+0x57
fffffa60`01b1dd80 00000000`00000000 nt!KiStartSystemThread+0x16
System(0xfffffa8003c8e950) has 124 active threads
THREAD fffffa8003ccebb0 Cid 0004.0070 Teb: 0000000000000000 Win32Thread: 0000000000000000 WAIT: (Suspended) KernelMode Non-Alertable
fffffa6001766600 SynchronizationEvent
Not impersonating
DeviceMap fffff88000007400
Owning Process fffffa8003c8e950 Image: System
Attached Process N/A Image: N/A
Wait Start TickCount 867788 Ticks: 632 (0:00:00:09.859)
Context Switch Count 904
UserTime 00:00:00.000
KernelTime 00:00:00.000
Win32 Start Address nt!KiExecuteDpc (0xfffff8000163c2e4)
Stack Init fffffa6001b24db0 Current fffffa6001b24a50
Base fffffa6001b25000 Limit fffffa6001b1f000 Call 0
Priority 31 BasePriority 8 PriorityDecrement 0 IoPriority 2 PagePriority 5
Child-SP RetAddr Call Site
fffffa60`01b24a90 fffff800`0166ef8a nt!KiSwapContext+0x7f
fffffa60`01b24bd0 fffff800`0167038a nt!KiSwapThread+0x2fa
fffffa60`01b24c40 fffff800`0163c48f nt!KeWaitForSingleObject+0x2da
fffffa60`01b24cd0 fffff800`0188cde3 nt!KiExecuteDpc+0x1ab
fffffa60`01b24d50 fffff800`016a3536 nt!PspSystemThreadStartup+0x57
fffffa60`01b24d80 00000000`00000000 nt!KiStartSystemThread+0x16
System(0xfffffa8003c8e950) has 124 active threads
THREAD fffffa8003cce720 Cid 0004.0074 Teb: 0000000000000000 Win32Thread: 0000000000000000 WAIT: (Suspended) KernelMode Non-Alertable
fffffa60017d5600 SynchronizationEvent
Not impersonating
DeviceMap fffff88000007400
Owning Process fffffa8003c8e950 Image: System
Attached Process N/A Image: N/A
Wait Start TickCount 867798 Ticks: 622 (0:00:00:09.703)
Context Switch Count 904
UserTime 00:00:00.000
KernelTime 00:00:00.000
Win32 Start Address nt!KiExecuteDpc (0xfffff8000163c2e4)
Stack Init fffffa6001b2bdb0 Current fffffa6001b2ba50
Base fffffa6001b2c000 Limit fffffa6001b26000 Call 0
Priority 31 BasePriority 8 PriorityDecrement 0 IoPriority 2 PagePriority 5
Child-SP RetAddr Call Site
fffffa60`01b2ba90 fffff800`0166ef8a nt!KiSwapContext+0x7f
fffffa60`01b2bbd0 fffff800`0167038a nt!KiSwapThread+0x2fa
fffffa60`01b2bc40 fffff800`0163c48f nt!KeWaitForSingleObject+0x2da
fffffa60`01b2bcd0 fffff800`0188cde3 nt!KiExecuteDpc+0x1ab
fffffa60`01b2bd50 fffff800`016a3536 nt!PspSystemThreadStartup+0x57
fffffa60`01b2bd80 00000000`00000000 nt!KiStartSystemThread+0x16
情報内の関数の動作を追ってみるとよりすっきりすっきりするかもしれません。なお、この情報は4コアーの環境下で取得しておりますが、2コアーでは当然2つのスレッドとなります。複数の異なる(Vista以降の)システム環境で採取したクラッシュダンプを同一の解析コードで解析してみますと、「Affinity」はシステムが厳密に定義していることが分かりました。魅力的な情報交換ありがとうございました。VREさま、いつも丁寧な解説感動しております。
私は部外者ですが、楽しく読ませていただいております。
- 編集済み ITDanwa-Kan 2011年3月23日 0:21 意味をより鮮明にするため
-
ITDanwa-Kan 様、この度は貴重なアドバイスを頂きありがとうございました。
私のようなへっぽこエンジニアの投稿を、ITDanwa-Kan 様のような凄い方に読んで頂いてるなんて、
夢にも思っておりませんでした。(恥ずかしいやら、嬉しいやら。。。。)「迷惑」なんてとんでもありません!! (そんなこと言ったら天誅が下ります。)
むしろ、ITDanwa-Kan 様のような方にアドバイスいただければ、多くのドライバ開発者が
救われると思います。
(少なくとも私は「インサイド Windows」に数え切れないほど救って頂きました。)ですので、お忙しいとは存じますが「部外者」なんて言わずに、これからもご教授頂ければと存じます。
今後とも、よろしくお願い申し上げます。