デバイスドライバその1

そういう訳で、IRQLの恩恵を蒙っているものの1つがデバイスドライバであることは論をまたない。そのデバイスドライバ、大雑把には「2.4.6 デバイスドライバ」(上巻P80~)で包括的に説明されているが、これはドライバの目的のみを記したレベルだ。そもそもマイコミジャーナルなどのレベルで一般にドライバの説明をする場合、上巻P62の図2-3の様な、大雑把な構造図が出てきて終わりだったりするが、これはドライバそのものの説明にはなっていない。

ドライバの構造は第9章のI/Oシステム(下巻P65~)に詳しい。特にドライバの内部については、「9.2 デバイスドライバ」(下巻P70~)からとなる。ちょっと「9.3.3 単層ドライバへのI/O要求」(下巻P104~)を例に、このあたりをもう少し深く突っ込んでみよう。

まずアプリケーションがI/O要求を出すと、それはDLLにトラップされてI/O Managerへのリクエストという形になる。このI/O Managerからの処理がKernel Modeでの処理となっている。I/O ManagerはI/O要求の詳細をまとめてIRP(I/O Request Packet)を作成し、これをドライバに対して送る形になる。このIRPはドライバのI/O開始ルーチンにキューイングされる形で蓄積されるわけだ。

そのドライバのI/O開始ルーチンは、キューイングされたIRPを検出したら、それを順次処理する。IRPを一つ取り出し、その内容にあわせてデバイスに実際にI/O処理を掛ける形になる。

さて、IRQLが関与してくるのはここからだ。そのデバイスがI/O処理を終わると、割り込みの形でそれを通知する形になるのが一般的だ。その割り込みを受けて、OSはISRを呼び出すが、この際にIRQLはDevice IRQLとなる。従ってそれまで実行していた作業は全てここで中断するわけだ。ただISRは既に説明したとおり最小限の処理しか行わなず、残りはPost Processで処理することになる。WindowsではこのPost Processの処理をDPC(Deffered Procedure Calls:遅延プロシージャ呼び出し)と称している。これを行うために、ISRはDPCで行うべき内容をDPCキューと呼ばれるものに追加して終了する。

ISRの処理が終わると本来IRQLは元に戻る筈だが、DPCキューがEmptyで無い場合、DPC割り込みが発生する。これにより、IRQLはDPC/Dispatchレベルに再び上がり、DPCキューにキューイングされた処理を順次処理してゆく事になる。

DPCルーチンは、内部的にはI/O Managerを呼び出してI/O完了処理を行うことになるが、もしアプリケーションでAPC(Asynchronous Procedure Call)の登録をしている場合、IRPの形でこのAPCがキューイングされる。このAPCが動くのは、DPC Routineやそこから呼び出されたI/O Managerの処理が完了した後になる。普通だとそのままIRQLはPassiveまで下がるが、APCがキューイングされていればAPC Interruptが発生し、この中でAPCが動作し、これが終わったところでやっとUser Applicationに制御が戻ることになる。

このあたりを時系列にそってまとめ直したのが図1である。実際にはこの説明はドライバのDispatch Routineの部分を端折っているのであんまり厳密ではないほか、グローバルデータの書き込みの同期を取るための仕組みとか、マルチプロセッサ環境での同期を取るための仕組みなども含まれている(下巻P110~P112)が、そのあたりはとりあえず措いておくと、概ねこんな感じでIRQLを上げたり下げたりしつつ、IRPと呼ばれる構造体を使って処理をしていると考えればよい。

図1

さて、このあたりでVMSの方を見てみることにしよう。図2は、やはり同じようにVMSに対してI/Oリクエストを出した場合の内部の動きを簡単にまとめたものだ。VMSの場合、全てのI/O命令はSYS$QIO(Queue I/Oの略)というシステムサービスを使い、このパラメータにデバイスの指定やI/Oの詳細を渡して行うことになる。Queue I/Oという名前の通り、原則として全てのI/Oは非同期となる。さて、IPL=0の状態でアプリケーションがSYS$QIOを発行すると、VMS Exective内部に用意されるEXE$QIOというルーチンが動き、ここが内部でFDT(Function Decision Table)Routineを呼び出す。このFDTはデバイス毎にどんな処理を行うかの記述を持っており、この中で対象のデバイスにあわせてIRP(I/O Request Packet)を生成し、それをデバイスドライバのStart I/O Routineにキューイングする。デバイスは、IRPがキューイングされると自動的にIRPのInterruptが動き、Start I/O Routineが起動してI/Oを開始することになる(続く)。

図2