none
DISPATCH_LEVELとDPC RRS feed

  • 質問

  • 基本的なことだと思うのですが、はっきりとわからなかったので、質問させて下さい。

    あるスレッドがDISPATCH_LEVELでIRPを送ってきたとします。
    IRPを処理中に、何らかのデバイスの割り込みが発生し(自分のデバイスでなくてもいい)、DIRQLに引き上げられて割り込みルーチンを処理します。
    その後、割り込みルーチンが処理を終えたときに、他にDIRQLで待機中のデバイスが無いとすると、再びDISPATCH_LEVELに戻ると思います。
    この時、DPCは実行されるのでしょうか?

    現在開発しているドライバで、IRPを処理しているときに上記のようなタイミングでDPCによって別のデバイスのCompleteRequestが発生し、まわりまわって自分のデバイスにIRPが発行されて再入しているのではないかという事を懸念しています。

    また、DPCを実行している間、スレッドは何になるのでしょうか?割り込まれる直前のスレッドか、DPCを専用に実行するスレッドか、不特定でしょうか?

    • 移動 Mike Wang (MSCS) 2012年10月2日 12:43 (移動元:Windows デバイスドライバー開発)
    2010年10月17日 13:56

回答

  • いままでそこまで深く掘り下げて考えてみたことはありませんけど、確かに言われてみれば気になるところですね。

    残念ながら、私にはこの質問に対して明確な返信をできるほどのスキルは無いのですが、以下は一応私なりの見解です。
    (参考になるかどうかわかりませんけど。。。)

    ご質問の趣旨は、
    「DISPATCH_LEVEL で処理中の IRP が、ISR などから生成された DPC に割り込まれることはあるのか?」
    と言うことだと思うのですが、個人的にはそれは「ない」と考えています。
    (もちろん、ドライバの作り方にも依存すると思いますが。。。)
    その理由としては、DPC も DISPATCH_LEVEL で動作するからです。
    もっと厳密に言うと、DPC とシステムのスレッド スケジューラも DISPATCH_LEVEL で動作するからです。
    この動作に関しては、下記サイトから正式な「日本語」ドキュメントも公開されています。

    ----------------------------------------
    スケジューリング、スレッド コンテキスト、および IRQL

    http://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 IRQLs

    http://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
    2010年10月18日 8:01

すべての返信

  • いままでそこまで深く掘り下げて考えてみたことはありませんけど、確かに言われてみれば気になるところですね。

    残念ながら、私にはこの質問に対して明確な返信をできるほどのスキルは無いのですが、以下は一応私なりの見解です。
    (参考になるかどうかわかりませんけど。。。)

    ご質問の趣旨は、
    「DISPATCH_LEVEL で処理中の IRP が、ISR などから生成された DPC に割り込まれることはあるのか?」
    と言うことだと思うのですが、個人的にはそれは「ない」と考えています。
    (もちろん、ドライバの作り方にも依存すると思いますが。。。)
    その理由としては、DPC も DISPATCH_LEVEL で動作するからです。
    もっと厳密に言うと、DPC とシステムのスレッド スケジューラも DISPATCH_LEVEL で動作するからです。
    この動作に関しては、下記サイトから正式な「日本語」ドキュメントも公開されています。

    ----------------------------------------
    スケジューリング、スレッド コンテキスト、および IRQL

    http://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 IRQLs

    http://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
    2010年10月18日 8:01
  • 回答ありがとうございます。

    「スケジューリング、スレッド コンテキスト、および 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で来ることを想定しているのですが、こういうことがあるんですね。意識していなかった (^^;

    とても参考になりました。ありがとうございます。

    2010年10月18日 8:38
  • この「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 意味をより鮮明にするため
    2011年3月21日 9:54
  • ITDanwa-Kan 様、この度は貴重なアドバイスを頂きありがとうございました。
    私のようなへっぽこエンジニアの投稿を、ITDanwa-Kan 様のような凄い方に読んで頂いてるなんて、
    夢にも思っておりませんでした。(恥ずかしいやら、嬉しいやら。。。。)

    「迷惑」なんてとんでもありません!! (そんなこと言ったら天誅が下ります。)
    むしろ、ITDanwa-Kan 様のような方にアドバイスいただければ、多くのドライバ開発者が
    救われると思います。
    (少なくとも私は「インサイド Windows」に数え切れないほど救って頂きました。)

    ですので、お忙しいとは存じますが「部外者」なんて言わずに、これからもご教授頂ければと存じます。
    今後とも、よろしくお願い申し上げます。

    2011年3月23日 2:42
  • VRE様

    ご丁寧な返信ありがとうございます。

    このような記事

    に目を通しますと、Microsoft社のWDKチームの皆様は「アーキテクチャとタスク」という2つの視点からDD開発者向けの情報を提供なさる方向で努力されているようです。私自身は「タスク」レベルの視座を持ちえませんから、「アーキテクチャ」視点から皆様の意見交換を拝読させていただき、時折、興味ある点をコードに実装し、その結果をご報告できれば、と考えております。

    本フォーラムの今後のご発展を楽しみにしております。

     

    2011年3月24日 1:21