はじめに
Linuxでファイルを読み書きすると、Applicationからの要求はFilesystem、Page Cache、Block Layerを経由します。その先で、Linux Kernelの共通的なI/O Requestを実際のStorage Hardwareが理解できるCommandへ変換するのがDevice Driverです。
HDD、SATA SSD、SAS SSD、NVMe SSDでは、接続InterfaceやCommand、Queueの構造が異なります。それでも上位のFilesystemが同じread()やwrite()を利用できるのは、Block LayerとDevice DriverがHardwareの違いを吸収しているためです。
また、データ転送ではCPUがすべてのByteを一つずつコピーし続けるのではなく、一般にDMAが利用されます。I/Oが完了するとDeviceはInterruptなどでCPUへ通知し、Device DriverからBlock Layer、Filesystem、Applicationへ結果が戻ります。
本記事では、LinuxのDevice Driverの役割、/devに見えるDevice Fileとの関係、Storage Controller、DMA、Interrupt、I/O完了処理までを一連の流れとして解説します。
Device Driverとは
Device Driverの役割
Device Driverは、Hardwareを制御するためのKernel内のSoftwareです。Deviceの初期化、I/O Commandの発行、Queueの管理、Interrupt処理、Error Recovery、Power Managementなどを担当します。
- 接続されたDeviceを検出して初期化する
- KernelのI/O RequestをHardware Commandへ変換する
- DMAに必要なMemory Mappingを準備する
- DeviceへCommandを送信する
- InterruptやPollingで完了を検出する
- 結果やErrorを上位レイヤーへ返す
Device DriverはKernelに組み込まれている場合と、Kernel Moduleとして必要時に読み込まれる場合があります。lsmodで読み込み済みModuleを確認できますが、Kernelへ組み込まれたDriverはlsmodに表示されません。
KernelとHardwareの橋渡し
FilesystemやBlock Layerは、Read、Write、Flushといった共通的な操作を扱います。一方、Hardware側ではRegister、Command Descriptor、Submission Queueなど、Interfaceごとに異なる方法で命令を受け取ります。
Device Driverは両者の間に入り、KernelのRequestをDevice固有の形式へ変換します。また、Hardwareから返されたCompletion StatusをLinuxが理解できる結果へ変換します。

なぜDevice Driverが必要なのか
DeviceごとにCommand形式、Register、Queue数、転送制限、Error処理が異なります。もしDriverによる抽象化がなければ、FilesystemやApplicationが各Hardwareの制御方法を個別に実装しなければなりません。
LinuxはDevice Driverと各SubsystemのInterfaceを定めることで、上位レイヤーとHardware固有処理を分離します。新しいStorage Deviceでも対応DriverがBlock Layerへ接続すれば、既存のFilesystemやApplicationからBlock Deviceとして利用できます。
Linuxから見たストレージデバイス
Block Deviceとして認識されるストレージ
LinuxはHDD、SSD、NVMe SSD、Virtual DiskなどをBlock Deviceとして扱います。Block Deviceは、論理的なSectorを指定して任意の位置へReadやWriteを実行できるDeviceです。
以下のコマンドを実行することで、各種情報を取得することができます。
lsblk
lsblkコマンドを実行することで、認識されたBlock Device、Partition、Size、Mount Pointなどを一覧表示できます。

| 項目 | 意味 |
|---|---|
| NAME | デバイス名。例:sda、sda1、sr0 |
| MAJ:MIN | Kernelがデバイスを識別するための Major番号 : Minor番号 |
| RM | Removable Deviceかどうか。1 = 取り外し可能、0 = 固定デバイス |
| SIZE | デバイスやPartitionの容量 |
| RO | Read Onlyかどうか。1 = 読み取り専用、0 = 読み書き可能 |
| TYPE | デバイスの種類。disk、part、rom、lvm など |
| MOUNTPOINTS | そのデバイスがMountされている場所。例:/、/boot |
本例では、sda が物理Disk、その配下の sda1・sda2 がPartitionという意味となります。
lspci
lspciではPCI接続されたStorage ControllerやNVMe Controllerを確認することができます。
lspci -nnk
-nn 名前 + 数値IDの両方を表示
-k Kernel Driver / Kernel Moduleを表示

上記の例では、Virtio SCSIのStorage Controllerが認識されており、Kernel driver in useからvirtio-pciが使用されていることを確認できます。
udevadm
udevadm infoではDeviceに関連する属性を確認できます。
udevadm info –query=all –name=/dev/sda

主要な項目を説明します。
| 項目 | 本例 | 意味 |
|---|---|---|
P: | /devices/pci0000:00/.../block/sda | sysfs上のDevice Path。PCI → Virtio → SCSI → Block Deviceという階層が分かる |
N: | sda | Device名 |
U: | block | 所属するSubsystem。Block Deviceであることを示す |
T: | disk | Device Type。Diskとして認識されている |
D: | b 8:0 | bはBlock Device、8:0はMajor / Minor Number |
DEVNAME | /dev/sda | /dev配下に作成されたDevice File |
ID_VENDOR | QEMU | DeviceのVendor |
ID_MODEL | QEMU_HARDDISK | DeviceのModel |
ID_BUS | scsi | SCSI Deviceとして認識されていることを示す |
ID_SERIAL | ...drive-scsi0 | Deviceを識別するSerial情報 |
DEVLINKS | /dev/disk/by-id/... など | /dev/sdaを指すSymbolic Link |
/dev/sda・/dev/vda・/dev/nvme0n1とは
| Device名の例 | 一般的な意味 | 関連するDriver Stackの例 |
|---|---|---|
| /dev/sda | SCSI Diskとして公開される最初のDisk。SATA、SAS、USB Storageなども該当し得る | sdとSCSI Mid-Layer、その下のHost Driver |
| /dev/vda | 仮想環境のVirtio Block Device | virtio_blk |
| /dev/nvme0n1 | NVMe Controller 0のNamespace 1 | NVMe Driver |
/dev/sdaという名前だけでは、物理的にSAS接続なのか、SATA Deviceがlibataを通じてSCSI Layerへ公開されたものなのか、USB Storageなのかを断定できません。Device名はLinuxから見える分類を表し、物理Interfaceの詳細とは必ずしも一致しません。
末尾に付くPartition名も命名規則によって異なります。/dev/sdaの最初のPartitionは/dev/sda1ですが、/dev/nvme0n1では/dev/nvme0n1p1のようにpを挟みます。
/devとDevice Driverの関係
/dev配下のFileはDevice Nodeと呼ばれ、Device Driverそのものではありません。User SpaceからKernel内のDeviceへアクセスするための入口です。Block Device NodeはMajor NumberとMinor Numberを持ち、Kernel内の対応するDeviceを識別します。
現在の一般的なLinuxでは、Kernelがsysfsへ公開したDevice情報をもとに、udevが/dev配下のNodeやSymbolic Linkを管理します。/dev/disk/by-uuidや/dev/disk/by-idのような永続的な名前を利用すると、検出順序で変わり得る/dev/sdaなどへの依存を減らせます。
Block LayerとDevice Driverの関係
blk-mqからDevice Driverへ
LinuxのBlock Layerでは、blk-mqがBlock I/O Requestを複数のQueueで管理します。Filesystemなどから提出されたbioはrequestへ関連付けられ、Software QueueとHardware Dispatch Queueを通じてDriverへ渡されます。
Driverはblk-mqが定めるInterfaceを実装し、Dispatchされたrequestを受け取ります。これにより、Block Layerは個々のHardwareのCommand形式を知らなくても、共通した方法でDriverへI/Oを渡せます。
I/O Requestが渡される流れ
Block Layerは、操作種別、開始Sector、Data Length、Memory Segment、Flagなどをrequestへまとめます。I/O Schedulerが有効であればDispatch順序を調整し、Hardware Queueへ送ります。noneが選ばれている場合は、高度な並べ替えを行わずにDispatchします。

DriverまたはDeviceが一時的に追加のCommandを受け付けられない場合、requestはQueueで待機します。Deviceへ送信可能になるとDispatchが再開されます。
Hardware Queueとの関係
blk-mqのHardware Queueは、Device Driverへrequestを渡すHardware Dispatch Queueです。必ずしも物理Hardware内のQueueそのものではありませんが、Driverは一般にDevice側のCommand Queueとの対応を考慮して設定します。
NVMeのように複数のSubmission QueueとCompletion Queueを持つDeviceでは、複数のHardware QueueをCPUへ分散して高い並列性を活用できます。一方、利用できるDevice Queueが少ない場合は、複数のCPUやSoftware Queueが同じHardware Queueを共有します。
Device DriverはI/O要求をどう処理するのか
Read / Write要求を受け取る
Device Driverはblk-mqからrequestを受け取ると、Read、Write、Flush、Discardなどの操作、対象Sector、転送するData Length、Memory Segmentを確認します。
さらに、Deviceの最大転送Size、Segment数、Alignment、Queue Depthなどの制限に適合していることを前提に処理します。多くの制限はBlock Layerへ事前に登録され、上位でのMergeやSplitにも利用されます。
デバイスが理解できるCommandへ変換する
Driverはrequestを、対象Interfaceが定めるCommandへ変換します。SATAではATA Command、SASではSCSI Command、NVMeではNVMe Commandが使用されます。実際にはTransport Driver、SCSI Mid-Layer、Controller Driverなど、複数層が変換に関与する場合があります。
| Block操作 | Device Commandの例 |
|---|---|
| Read | 指定Logical BlockからMemoryへデータを転送するCommand |
| Write | Memoryのデータを指定Logical Blockへ書き込むCommand |
| Flush | Deviceの揮発性Write Cacheを不揮発媒体へ反映するCommand |
| Discard | 使用しなくなったLogical Block RangeをDeviceへ通知するCommand |
すべてのDeviceがすべての操作へ対応するとは限りません。DriverはDeviceのCapabilityを検出し、上位レイヤーへ公開します。
HardwareへCommandを発行する
DriverはCommand DescriptorやDMA対象の情報をMemoryへ準備し、Deviceから参照できる状態にします。その後、Memory-Mapped I/O Registerへの書き込みやDoorbell通知など、Interface固有の方法でControllerへCommandの存在を知らせます。
Commandを発行したrequestはIn-Flight状態となり、Driverとblk-mqがTagなどで追跡します。Deviceが同時に処理できるQueue Depthへ達している場合、新しいrequestは空きが生じるまで待機します。
Storage Controllerとストレージデバイス
Storage Controllerの役割
Storage Controllerは、Host側からCommandを受け取り、Storage MediaへのReadやWriteを制御します。Command Queueの管理、DMA、Error Detection、Retry、Cache管理などを担当します。
Controllerの位置や形態は構成によって異なります。SATA Host Controllerの先に個別のDriveが接続される場合、SAS HBAやHardware RAID Controllerを使う場合、NVMe SSD内にControllerが組み込まれてPCIeへ直接接続される場合などがあります。
SATA / SAS / NVMe
| Interface | 主な特徴 | 一般的な利用例 |
|---|---|---|
| SATA | AHCIなどを通じて接続され、Queueの並列性はNVMeより限定的 | Consumer向けHDD・SSD、一般Server |
| SAS | Enterprise向け機能、Dual Port、Expanderなどに対応する構成がある | Server向けHDD・SSD、Storage Array |
| NVMe | PCIe向けに設計され、多数のQueueと低Latencyを重視する | 高性能SSD、Database、仮想化基盤 |
SATAとSASは似たDevice名で見えることがありますが、ProtocolやController Stackは異なります。NVMeは従来のSCSI Disk名ではなく、ControllerとNamespaceを表す独自のDevice名を使用します。
Linux KernelからStorageまでの位置関係
Linux Kernel内のDevice DriverはControllerを操作し、ControllerがStorage MediaとのCommand実行やData Transferを進めます。

ただし、NVMe SSDのようにControllerとFlash Mediaが一つのDevice内に収まる場合もあるため、ControllerとStorage Deviceが常に別筐体というわけではありません。
Virtual Machineにおける物理StorageまでのI/Oの流れ
Virtual Machineの/dev/vdaでは、Guest Kernelのvirtio DriverからVirtual Deviceへ要求が渡り、Host側のHypervisorやBackendがさらにHostのFilesystemまたはBlock Deviceへ処理します。この場合、物理Storageまでに複数のSoftware Layerが存在します。

DMAの仕組み
DMAとは
DMAはDirect Memory Accessの略で、DeviceがCPUによる逐次的なData Copyを介さず、Memoryとの間でDataを転送する仕組みです。Storage I/OだけでなくNetworkやGraphicsなど、多くの高速Deviceで利用されます。
CPUは転送対象、方向、SizeなどをDriver経由で設定し、ControllerへCommandを発行します。転送そのものはDMA Engineが進め、完了時にCPUへ通知します。

なぜCPUがデータを直接コピーし続けないのか
CPUがStorage InterfaceからMemoryへDataを一つずつコピーし続けると、転送中は多くのCPU時間を消費します。大容量Dataや多数のI/Oを扱うほど、Applicationの計算などに使えるCPU時間が減ってしまいます。
DMAを使えば、CPUは転送を設定した後に別のProcessを実行できます。転送が終わった時点で完了処理を行えばよいため、CPUとI/Oを並行して進められます。
MemoryとStorage間でデータが転送される仕組み
Driverはrequestに含まれるMemory SegmentをDMA用にMapし、Deviceが利用できるDMA Addressを取得します。物理的に連続していない複数のMemory領域は、Scatter-Gather ListとしてControllerへ渡せる場合があります。

IOMMUが有効な環境では、Deviceが使用するI/O Virtual Addressと物理MemoryをIOMMUが対応付けます。これにより、Deviceがアクセスできる範囲を制限し、Virtual MachineへのDevice割り当てなども支援できます。
DMAを使ってもCPU処理が完全になくなるわけではありません。DriverによるMapping、Command発行、Cache Coherencyへの対応、完了処理などは必要です。また、HardwareやArchitectureによってDMAとCPU Cacheの整合性を保つ方法は異なります。
I/O完了はLinuxへどう通知されるのか
Storage DeviceによるI/O処理
Storage DeviceはDriverから受け取ったCommandを処理します。Readでは指定されたLogical BlockからDataを取得してMemoryへ転送し、WriteではMemoryから受け取ったDataをDevice側へ書き込みます。
HDDではHeadのSeekやPlatterの回転待ちが発生します。SSDではControllerがLogical AddressをNAND Flashの位置へ変換し、Garbage CollectionやWear Levelingなども内部で管理します。
Interruptによる完了通知
I/Oが完了すると、ControllerはCompletion Statusを記録し、一般にInterruptを発生させてCPUへ通知します。PCIe DeviceではMSIまたはMSI-Xが使われ、複数QueueのInterruptを複数CPUへ分散できる場合があります。
Interruptごとに大きな処理を行うと、高IOPS環境ではCPU負荷が増えます。そのため、複数Completionをまとめて処理するInterrupt Coalescingや、Interruptを使わずにCompletionを確認するPollingが利用されることもあります。
Device Driverによる完了処理
Interruptを受けたDriverはControllerのCompletion情報を確認し、完了したCommandと対応するrequestを特定します。そして、Status Code、転送量、Errorの有無などを確認します。
DMA Mappingを解除するなどの後処理を行い、必要に応じてError RecoveryやRetryを開始します。Timeout、Media Error、Transport Errorなど、Errorの種類によって対応は異なります。
Block Layerへ完了を返す
Driverはblk-mqへrequestの完了を通知します。Block Layerはrequestに含まれるbioへ結果を反映し、FilesystemやPage Cacheなどの上位レイヤーの完了処理を呼び出します。
Readの場合はDataが格納されたPageを利用可能にし、同期read()で待っていたProcessを起床させます。Writebackの場合は書き出し中のPageを更新し、正常に反映されたDirty PageをCleanな状態へ移します。
Processは起床直後に必ず実行されるわけではありません。Runnableな状態となり、CPU SchedulerによってCPUを割り当てられた後に処理を再開します。
HDD・SSD・NVMeで何が違うのか
| Device | 記録媒体 | 主な特性 |
|---|---|---|
| HDD | 回転する磁気Disk | 大容量・低単価だが、Seekと回転待ちによりRandom I/OのLatencyが大きい |
| SATA SSD | NAND Flash | 機械的動作がなく、HDDよりRandom I/Oが高速。SATA Interfaceの上限を受ける |
| NVMe SSD | NAND Flashなど | PCIeと多数のQueueを利用し、高IOPS・低Latency・高い並列性を実現する |
同じSSDでも、SATA SSDとNVMe SSDではHostとの接続方式とCommand Protocolが異なります。Flash Memory自体の特性だけでなく、Interface、Controller、Queue構造が性能差に影響します。
接続インターフェースの違い
SATAは従来のStorage向けInterfaceとして広く利用され、AHCIと組み合わせる構成が一般的です。SASはEnterprise向けの接続性や冗長構成に適した機能を持ちます。
NVMeはNon-Volatile Memory向けに設計され、PCIe経由で接続します。複数のSubmission QueueとCompletion Queueを利用できるため、多数CPUからのI/Oを並列に処理しやすい構造です。
Device Driverの違い
SATA Deviceは一般にlibataとSCSI Subsystemを経由し、Diskとしてsd Driverから公開されます。SASではSCSI Mid-LayerとHBA固有Driverなどが連携します。NVMeではNVMe DriverがControllerとNamespaceを管理します。
Virtual Diskではvirtio_blk、virtio_scsi、Hypervisor固有のDriverなどが使用される場合があります。Cloud上で/dev/nvme0n1と見えても、物理的なNVMe Deviceを専有しているとは限らず、仮想化されたStorage ServiceへのInterfaceである場合があります。
現在Deviceへ関連付けられているDriverは、lspci -k、udevadm info、/sys/class/block配下のSymbolic Linkなどをたどって確認できます。Device名だけでDriverや物理構成を推測せず、sysfs情報を確認することが重要です。
Device DriverからStorageまでの流れを整理する

Block Layerとblk-mqがrequestをDispatchすると、Device DriverがReadやWriteなどの内容を確認し、Hardwareが理解できるCommandを作成します。DriverはDMA Mappingを準備し、Storage ControllerへCommandを通知します。
ControllerとStorage DeviceはCommandを処理し、DMAによってMemoryとの間でDataを転送します。転送とDevice側の処理が完了すると、一般にInterruptによってCPUへ通知します。
Device DriverはCompletion Statusを読み取り、対応するrequestを特定してBlock Layerへ完了を返します。Block Layerはbioを完了させ、FilesystemやPage Cacheへ結果を伝えます。同期I/Oで待っていたApplicationは、その後CPUへSchedulingされると処理を再開します。
図では理解しやすいようにStorage ControllerとStorage Deviceを分けていますが、両者の境界は構成によって異なります。また、高速I/OではInterruptの代わりにPollingを利用する場合があり、Virtual StorageではHost側に追加のDriverやBackend Layerが存在します。
まとめ
Device Driverは、Linux KernelとHardwareの間を結ぶSoftwareです。Block Layerから受け取った共通的なI/O RequestをSATA、SAS、NVMeなどのDeviceが理解できるCommandへ変換し、Commandの送信、DMA、Interrupt、Error処理を管理します。
/dev/sda、/dev/vda、/dev/nvme0n1はUser SpaceからBlock DeviceへアクセスするためのDevice Nodeであり、Driver本体ではありません。Device名はLinuxから見える分類を示しますが、物理的な接続やDriver Stackを正確に知るにはsysfs、udevadm、lspciなどによる確認が必要です。
データ転送では一般にDMAが使われるため、CPUは転送中に別の処理を実行できます。I/Oが完了するとInterruptまたはPollingでDriverが結果を検出し、blk-mq、Block Layer、Filesystem、Page Cacheへ完了を返します。
HDD、SATA SSD、NVMe SSDでは、MediaだけでなくInterface、Queue構造、Controller、Driver Stackが異なります。Device DriverからStorageまでの流れを理解することで、I/O Latency、Queue詰まり、Interrupt負荷、Driver Errorなどをレイヤーごとに切り分けやすくなります。


コメント