デバイスドライバとは、OSの共通的なI/O要求を、特定機器のコマンド・記述子・レジスタ操作へ変換し、完了結果やエラーをOSの共通形式へ戻すソフトウェアです。
「OSとハードウェアの橋渡し」という説明は正しい入口ですが、橋を渡るだけではありません。ドライバは要求をキューへ割り当て、機器がアクセスできるメモリアドレスを準備し、開始を通知し、割り込みまたはポーリングで完了を回収します。
この記事では、テキストエディタでmemo.txtへAを追記する例を、LinuxのブロックI/OとPCIe接続のNVMe SSDを具体例として最後まで追います。NVMe固有の用語は一般原理を見える形にするための実装例であり、USB機器、GPU、ネットワークアダプターではコマンドや転送方式が異なります。

ドライバは「翻訳機」に例えられます。ただし翻訳するのは文章ではなく、操作、アドレス、キュー、完了状態です。「どこへ何バイト書く」という共通要求を、「この装置のこの形式で、ここへ並べ、開始レジスタを更新する」へ具体化します。
ドライバが変換するのは「要求」と「完了」
アプリは「NVMe Submission Queueの何番へ命令を置くか」や「どのMMIOレジスタを更新するか」を指定しません。ファイルへ書くという意味をOSへ渡し、ファイルシステムやブロック層が機器に依存しない要求を組み立てます。
| ドライバへ届く共通的な情報 | 機器固有へ具体化する例 | 完了時に共通形式へ戻すもの |
|---|---|---|
| 読み取り/書き込み等の操作 | NVMe Write等のopcode | 成功、再試行可能な失敗、致命的な失敗 |
| 論理ブロック範囲と長さ | namespace、開始LBA、ブロック数 | 処理済み量、残量、エラー位置 |
| データがあるRAM領域 | DMA mapping、PRP/SGL等のデータポインタ | DMA完了、mappingの解放 |
| 同期・優先度・flush等の属性 | command field、queue選択、制御フラグ | 装置statusをOSのエラーへ対応付け |
| 要求を識別する情報 | queue slot、command ID、tag | どの待機中要求が終わったか照合 |
つまり、ドライバはデータ形式だけを変えるのではありません。OSが管理する要求のライフサイクルを、装置が理解するプロトコルのライフサイクルへ対応付けるのが中心です。

memo.txtのAがSSDへ届くまで
ここでは、ページキャッシュ上の変更をストレージへ書き戻す段階から見ます。保存ボタン、write成功、媒体への永続化は同じ時点ではありません。この前段はファイルシステムとは、待ちとflushの境界はバッファとはで詳しく説明しています。
1. VFS・ファイルシステム・ブロック層が共通requestを作る
テキストエディタはシステムコールの入口からOSへ書き込みを依頼します。VFSとファイルシステムはパス、開いたファイル、権限、論理オフセット、ファイルシステム上の割り当てを扱います。その結果、ストレージへ必要な論理ブロック範囲とデータのあるメモリ領域がI/O要求になります。
Linuxのblk-mqでは、bioからブロックドライバへ渡すstruct requestが作られ、ソフトウェア側キューとハードウェアdispatch queueで管理されます。この段階の「write request」は、NVMeやSATAの細かな命令形式にはまだ依存しない共通モデルです。
2. ドライバがqueue・tag・command descriptorを割り当てる
ブロック層がNVMeドライバのqueue_rq相当を呼ぶと、ドライバは空いているqueue slotとtagを確保し、共通requestからNVMe Write commandを作ります。開始LBA、ブロック数、namespace、command ID、制御フラグなどを、NVMe仕様で定められたcommand entryへ配置します。
tagやcommand IDが重要なのは、複数の要求を同時に進め、完了順が投入順と異なっても、どのOS requestが終わったかを特定するためです。NVMe仕様でも、commandは順不同で完了し得ると定められています。
3. DMA mappingで「装置から見えるRAMの住所」を作る
CPUが使う仮想アドレスを、そのままPCIe装置が使えるとは限りません。ドライバはOSのDMA APIへRAM領域を渡し、IOMMU等で必要な対応付けを作り、装置が使うDMA addressを受け取ります。NVMe commandにはPRPやSGLのようなデータポインタとして、この転送先情報を載せます。
ここでの重要点は、ドライバが毎回CPUでデータ本体を装置へコピーするとは限らないことです。ドライバは主に転送可能な領域とアドレスを準備し、DMA engineを持つコントローラがRAMと装置の間でデータを転送します。CPUは設定と完了処理を担います。
4. commandをSubmission Queueへ置き、doorbellを鳴らす
PCIe接続NVMeでは、Submission QueueとCompletion Queueはホストからアクセスできるメモリ上に置かれます。ドライバはcommand entryをSubmission Queueへ書き、必要な順序を保証したうえで、MMIOのSubmission Queue Tail Doorbellレジスタを更新します。
doorbellはデータ本体ではなく、「queueのtailがここまで進み、新しいcommandがある」ことをコントローラへ知らせる制御信号です。Linuxでは、機器のMMIOレジスタへアクセスするためにioremap、readl、writel等のAPIが使われます。バスの順序規則があるため、普通のメモリ変数と同じ書き方では扱えません。
5. コントローラとファームウェアがcommandを実行する
NVMeコントローラはdoorbellの更新を知るとSubmission Queueからcommandを取得し、DMAでRAM上のデータを読み、装置内部で処理します。SSDのファームウェアはコントローラ内のプロセッサ等で動き、論理ブロックをNAND上の配置へ対応付け、誤り訂正、ウェアレベリング、ガベージコレクションなどを制御します。
ドライバがNANDセルへ直接電荷を書き込むわけではありません。ドライバはホスト側でNVMeという公開インターフェースを扱い、コントローラとファームウェアが装置内部の詳細を扱います。物理的な保持原理はストレージとはへ分離します。
6. Completion Queueへstatusが書かれ、割り込みまたはpollingで回収する
commandが完了すると、コントローラは対応するCompletion Queueへcompletion entryを書きます。そこには、どのSubmission Queue・command IDに対応するか、成功かエラーか、といったstatusが入ります。
コントローラはMSI-X等の割り込みで「新しいcompletionがある」とCPUへ通知できます。ただし、割り込みをまとめるcoalescingや、ドライバがqueueを能動的に確認するpollingを使う場合もあり、1要求につき必ず1割り込みとは限りません。
7. ドライバが装置statusをOSのcompletionへ戻す
ドライバはcompletion entryのqueue IDとcommand ID/tagから元のrequestを特定し、DMA mappingやqueue slotを解放します。装置固有statusを、成功、I/O error、timeout、再試行、媒体エラー等のOS側statusへ対応付け、ブロック層へ完了を返します。
その上でファイルシステムや待機中の処理が完了を受け取ります。ページキャッシュを使う通常の書き込みでは、アプリのwriteがこの媒体I/Oより前に成功している場合があります。一方、同期的な永続化要求なら、この下位completionを待つ経路が重要になります。
| 境界 | 渡すもの | その時点でまだ終わっていないこと |
|---|---|---|
| ファイルシステム → ブロック層 | 論理ブロック範囲、RAM上のデータ、I/O属性 | 機器固有commandの作成 |
| ブロック層 → ドライバ | 共通requestとqueue情報 | DMA mapping、descriptor、MMIO通知 |
| ドライバ → コントローラ | Submission Queue entry、DMA address、doorbell | command実行、媒体反映 |
| コントローラ → ドライバ | Completion Queue entry、status、interrupt等 | OS requestへの対応付け、上位への完了 |
| ドライバ → ブロック層 | 共通status、処理済み量、解放可能な資源 | ファイルシステム・アプリへの最終通知 |
5つの境界を混ぜない
「ハードウェア側」という一言には、異なる役割が含まれます。境界を分けると、ドライバがどこまで担当するかが見えます。
| 構成要素 | 中心役割 | NVMe SSDの例 | 担当しないことの例 |
|---|---|---|---|
| OSサブシステム | 機器に依存しない抽象化、保護、共有、要求管理 | VFS、ファイルシステム、page cache、block layer | 個別SSDのdoorbell形式 |
| デバイスドライバ | 共通requestと機器固有protocolの対応付け | NVMe command作成、DMA mapping、queue、MMIO、completion処理 | ファイル名のパス探索、NANDセル配置の内部アルゴリズム |
| バス/プロトコル | 接続、列挙、アドレス、転送、順序、command形式の規則 | PCIe transport、NVMe command set | 特定OSでのrequest lifecycle |
| デバイスコントローラ | host commandを受け、DMA engineや媒体制御回路を動かす | Submission Queue取得、DMA、Completion Queue更新 | OSのファイル権限判断 |
| ファームウェア | 機器内部のプロセッサ等で制御ロジックを実行 | FTL、誤り訂正、ウェアレベリング、回復処理 | Windows/Linuxのdriver API実装 |
ドライバとファームウェアは協調しますが、同じものではありません。ドライバは通常ホストOS側、ファームウェアは通常デバイス内部で動きます。両者のversionや機能交渉が合わなければ、認識はしても一部機能が使えない、復帰に失敗する、timeoutになる、といった問題が起き得ます。
Plug and Playは「ID照合 → 初期化 → 資源割り当て」
機器を接続すると、まずPCIe、USB等のバス側が機器を列挙し、device ID、compatible ID、MMIO範囲、interrupt等の資源情報をOSへ知らせます。OSは、そのIDをサポートすると宣言するドライバを探します。
Linuxのdriver modelでは、bus固有のmatchでdevice IDとdriverの対応を調べ、一致するとdriverのprobeを呼びます。probeは実機を確認し、deviceを有効化し、MMIOをmapし、DMA条件を設定し、queueやinterrupt handlerを準備します。成功して初めてdeviceとdriverがbindされます。
Windowsでも、機器が報告したhardware ID/compatible IDとdriver packageのINFを照合します。候補が複数あると、署名、機能、IDの一致度等を含むrankが優先され、同rankなら日付・versionが比較されます。単にversion番号が最大のdriverを無条件で選ぶわけではありません。
すべてのドライバがカーネルモードとは限らない
MMIO、DMA、interrupt、電源管理などへ直接関わる低水準driverは、強い権限と短い応答時間が必要なためkernel modeで動くことが多いです。しかし、「driver=必ずkernel mode」ではありません。
Windows Driver FrameworksにはKMDF(Kernel-Mode Driver Framework)とUMDF(User-Mode Driver Framework)があります。UMDF driverはdriver host processで動き、I/O requestやPlug and Play通知を受けます。必要ならkernel mode部分とuser mode部分に分ける構成も可能です。
| 観点 | カーネルモードdriver | ユーザーモードdriver |
|---|---|---|
| アクセス範囲 | OS内部資源や低水準hardware機能へ広くアクセスできる | frameworkが許可するinterfaceとuser spaceの範囲が中心 |
| 障害影響 | 不正なmemory accessでOS全体をcrashさせ得る | 主にdriver host processの失敗として隔離・再起動できる場合がある |
| 向く処理 | boot、厳しいlatency、直接interrupt/DMA等が必要な処理 | frameworkで扱えるUSB等、隔離を優先できる処理 |
| 代償 | 権限と障害範囲が大きい | 使える機能や対象deviceに制約があり、遷移のoverheadもある |
Microsoftの公式説明では、kernel mode codeはOSや他driverと同じvirtual address spaceを共有するため、誤ったaddressへの書き込みやcrashがOS全体へ影響します。UMDFではdriver failureを検出し、device stackを再構築してdriverを再起動または無効化する仕組みがあります。
署名・権限・互換性は別々に確認する
署名は「出所と改ざん検出」であり、無欠陥の保証ではない
driverは高い権限で動く場合があるため、OSはdriver packageの署名と信頼経路を確認します。現在のWindowsでは、新しいkernel-mode driverをloadする際にMicrosoftの署名policyが適用されます。
ただし、正しく署名されたdriverにもbugや脆弱性はあり得ます。署名は主に発行者・配布経路・改ざんを検証する材料であり、性能向上、完全な安全性、対象deviceへの最適性を自動的に保証しません。
権限が大きいのは、機器がmemoryとOS状態へ影響できるから
DMA対応deviceは、設定されたaddressへCPUを介さずアクセスできます。driverがmapping範囲、転送方向、lifetime、cache同期を誤れば、別データの破壊や漏えいにつながります。MMIO registerの誤操作も、device停止、誤転送、interrupt storm等を起こし得ます。
互換性はdevice IDだけでは決まらない
| 確認軸 | 合わないと起きること |
|---|---|
| device/compatible ID | driver候補にならない、誤った機能前提で初期化に失敗する |
| OS、kernel、driver framework、ABI | load失敗、symbol不一致、callback contract不一致 |
| CPU architecture | x64 binaryをArm64でloadできない等 |
| driverとfirmwareのversion/feature | commandやpower stateの解釈が合わずtimeout・機能欠落 |
| 署名policyとboot security | OSがloadを拒否する |
| 他のfilter/class/bus driver | stack内の順序・interface・resource競合 |
とくにLinuxのkernel内部interfaceは、userspace ABIとは異なり、すべてのversionで安定したbinary driver ABIを提供する設計ではありません。配布kernel外で作られたbinary moduleは、kernel更新に合わせた再buildや提供元の対応が必要な場合があります。
driver更新で必ず速くなるわけではない
driver更新の目的は、性能向上だけではありません。脆弱性修正、crash修正、新しいOSへの対応、新device IDの追加、power management改善、既知の回避策、telemetry改善などがあります。
性能が変わるのは、driverのqueueing、interrupt coalescing、DMA、offload、error recovery等が実際のbottleneckだった場合です。CPU、bus帯域、controller、媒体、filesystem、applicationが律速なら、driver更新だけで速くなるとは限りません。新versionが特定環境でregressionを起こす可能性もあります。
| 更新前 | 更新後 |
|---|---|
| 変更理由と対象不具合をrelease noteで確認 | device認識、error log、sleep復帰、I/O timeoutを確認 |
| hardware ID、OS、architecture、firmware条件を確認 | 必要なworkloadでlatency・throughput・CPU使用率を比較 |
| 正規packageと署名を確認 | 新しいwarning、drop、retry、resetが増えていないか確認 |
| 旧版、復元点、rescue手段を確保 | 問題時はrollbackし、firmwareやhardwareも切り分ける |
機器が動かないときは、往路と復路を分けて見る
「driverの問題」と一括りにせず、要求がどこまで進み、completionがどこで止まったかを確認します。
| 観測地点 | 典型的な症状 | 確認するもの |
|---|---|---|
| 列挙・matching | device自体が一覧に出ない、unknown device | 電源、接続、bus、device ID、INF/ID table |
| probe・初期化 | 認識するがdriver bind失敗 | MMIO resource、DMA mask、firmware load、interrupt割当 |
| request queue | I/O開始後に待ちが増える | queue depth、resource不足、timeout、backpressure |
| DMA・MMIO | data corruption、IOMMU fault、commandが始まらない | mapping方向・範囲・lifetime、doorbell、memory ordering |
| controller/media | completionにdevice error、resetが続く | SMART/health、firmware、温度、媒体、電源 |
| interrupt/polling | deviceは処理したのにOSがtimeout | MSI-X vector、interrupt mask、completion queue、poll path |
| error mapping・上位層 | 原因が曖昧なI/O errorとして見える | driver log、kernel event、block/filesystem log |
WindowsではDevice Managerのhardware ID、driver provider/version、status code、Event Viewerが入口です。Linuxではlspci -kやlsusbでdeviceと使用中driverを確認し、dmesgやjournalでprobe、IOMMU、timeout、reset、I/O errorを探します。管理者権限や追加toolが必要な場合があります。
よくある誤解を仕組みから直す
| 誤解 | 正しい捉え方 |
|---|---|
| driverがdataを必ずCPUでdeviceへcopyする | driverはDMA mappingとdescriptorを準備し、controllerがRAMとの転送を行う場合が多い。PIOやbounce buffer等の例外もある |
| すべてのdriverがkernel modeで動く | 低水準driverはkernel modeが多いが、UMDF等のuser-mode driverや分割構成もある |
| driverとfirmwareは同じもの | driverは主にhost OS側、firmwareは主にdevice内部。公開protocolを境界に協調する |
| driver更新は新しいほど必ず速い | 目的はsecurity、bug、compatibility、power等もある。bottleneckがdriverでなければ性能は変わらず、regressionもあり得る |
| doorbellを書けばdata保存完了 | doorbellは新しいcommandをcontrollerへ知らせるだけ。その後にDMA、実行、media処理、completionがある |
| 1 requestごとに必ずinterruptが来る | MSI-X等を使えるが、interrupt coalescingやpollingもある。completion queueの確認が本体 |
| OSは最もversion番号が大きいdriverを選ぶ | まずdevice IDの一致度、署名、機能等で候補をrank付けし、同rank等の条件で日付・versionを比較する |
| 署名済みならbugも脆弱性もない | 署名は出所・改ざん・load policyの材料。code品質や対象環境での正しさを保証しない |
まとめ:driverは要求と完了の対応表を実機へつなぐ
デバイスドライバの本質は、OSと機器の間に存在することではなく、両側の「要求の表し方」と「完了の表し方」を対応付けることです。memo.txtのAについて、共通requestをcommandへ変え、RAMをDMA可能にし、doorbellで開始を知らせ、completionを元requestへ戻せれば、「橋渡し」の中身を説明できます。
関連する基礎用語
- OSとは:driverを含む抽象化・共有・保護の全体像を確認する
- I/O(入出力)とは:request queue、DMA、interrupt、pollingという共通I/O機構を確認する
- システムコールとは:アプリの要求がuser modeからkernelへ入る境界を確認する
- ファイルシステムとは:ファイル名、logical block、page cache、writebackの前段を確認する
- バッファとは:各queueの容量、待ち、backpressure、flushを確認する
- ストレージとは:controller、firmware、NAND/磁気媒体、永続化の後段を確認する
