🔍
B站相應的視屏教程:
📌 內核:博文+視頻 - 總線驅動模型實戰全解析
敬請關注,記得標為原始粉絲。
🔧
在上篇中,我們已經從理論視角分析了“虛擬總線驅動模型”在 Linux 驅動體系中的獨特定位。本篇我們聚焦實戰:深入分析一個真實的內核子系統 —— virtio_blk
虛擬塊設備驅動,完整講清虛擬總線模型的運行機制、設備匹配、驅動注冊、驅動結構體組織方式、probe 流程、VQ(virtqueue)使用方式等。我們將對照 platform_driver、i2c_driver,總結異同點,幫助你徹底理解這個典型的虛擬驅動模型。
1. virtio_blk 簡介與實戰目標
virtio_blk
是 Linux 內核中 VirtIO 虛擬設備框架的一部分,模擬了一個塊設備(類似硬盤),用于 KVM 虛擬機中的磁盤訪問、容器虛擬塊設備等場景。它不對應具體硬件,但提供了完整的設備模型。
我們實戰目標是:
- 看懂
virtio_blk
驅動如何注冊。 - 理解它是如何匹配“虛擬設備”的。
- 弄清 virtio 總線下設備與驅動之間的綁定機制。
- 對比
platform_driver
和virtio_driver
,理解它們的核心區別。
2. virtio 驅動注冊機制總覽
在 drivers/block/virtio_blk.c
中,驅動最終通過以下結構注冊:
static struct virtio_driver virtio_blk = {.feature_table = features,.feature_table_size = ARRAY_SIZE(features),.driver.name = KBUILD_MODNAME,.id_table = id_table,.probe = virtblk_probe,.remove = virtblk_remove,
};
通過:
module_virtio_driver(virtio_blk);
內核完成注冊,最終宏會展開為 module_init()
和 module_exit()
自動注冊。
這非常類似于 platform_driver
的注冊過程:
module_platform_driver(my_platform_driver);
區別在于總線類型不同:
platform_driver
注冊到 platform 總線上,匹配platform_device
。virtio_driver
注冊到 virtio 總線上,匹配virtio_device
。
3. virtio 設備和驅動匹配機制
virtio 的匹配方式不基于設備樹(也可以配合使用),而是通過 virtio_device_id
表完成。
static const struct virtio_device_id id_table[] = {{ VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID },{ 0 },
};
VIRTIO_ID_BLOCK
是預定義的設備類型標識,代表塊設備。- 系統在虛擬機啟動時(比如 QEMU)會注入
virtio_device
,并調用驅動的probe
。
匹配過程:
virtio_bus.c→ virtio_register_driver()→ __register_driver()→ bus_add_driver()→ driver_match_device() // 比較 virtio_id
和 platform_driver
的 of_match_table
匹配方式略有不同,virtio_driver
更偏向于“協議棧類型匹配”。
4. 重點函數分析:virtblk_probe()
static int virtblk_probe(struct virtio_device *vdev) {// 關鍵:分配 virtio_blk 結構體,掛載到 vdev->privvdev->priv = vblk = kmalloc(...);// 初始化 virtqueueinit_vq(vblk);// 分配 gendisk,注冊 blk-mq 調度器vblk->disk = blk_mq_alloc_disk(...);// 注冊設備device_add_disk(...);
}
vdev->priv
與 platform_driver 中的dev_set_drvdata()
類似,保存上下文。virtqueue
是 virtio 驅動的關鍵數據通道。- 使用
blk-mq
接口建立請求調度與提交。
5. virtqueue 與傳統 platform 驅動的差異
項目 | virtio_blk | platform_driver |
---|---|---|
總線類型 | virtio 總線 | platform 總線 |
匹配機制 | virtio_device_id | of_match_table / id_table |
資源傳遞 | virtqueue + virtio_config | 設備樹 + platform_resource |
probe 中行為 | 初始化 VQ / 隊列注冊 | 申請 IO 內存、中斷、寄存器 |
特點 | 無真實硬件,支持熱插拔 | 通常為靜態資源 |
6. 真實代碼結構分析:virtio_blk 是怎樣注冊塊設備的?
從注冊 gendisk
到 blk_mq
調度器配置:
vblk->tag_set.ops = &virtio_mq_ops;
blk_mq_alloc_tag_set(&vblk->tag_set);
vblk->disk = blk_mq_alloc_disk(&vblk->tag_set, vblk);
重點在于:
virtio_mq_ops
提供了.queue_rq
、.poll
、.map_queues
等接口。- 驅動注冊時,通過
device_add_disk()
向 block subsystem 注冊。
再看看 .queue_rq
實現:
virtio_queue_rq() {struct virtblk_req *vbr = blk_mq_rq_to_pdu(req);virtblk_add_req(...) // 將請求掛到 virtqueue
}
即使沒有真實硬件,virtqueue
就相當于“虛擬的 DMA ring”,請求通過它發送到對端。
7. 與軟件工程中的“適配器”模式對比分析
設備模型是內核層次的一種“適配機制”:
- 驅動與設備解耦(device 和 driver 通過總線進行匹配)。
- 匹配后注冊調用 probe(相當于運行時建立適配連接)。
- 通過統一接口調用函數(如
probe()
、remove()
、suspend()
)。
在 virtio 中,這種適配機制更為純粹,因為:
- 驅動和設備都是“運行時注入”的;
- 沒有真實硬件,不需要解析物理寄存器映射;
- 強依賴
virtqueue
來傳遞數據,適配的是協議數據格式。
因此,virtio 驅動更像是“運行時適配器 + 抽象接口定義”的組合,非常符合軟件工程中 Adapter 模式的精髓。
8. 總結與核心問答
Q1:virtio_driver 和 platform_driver 最大區別是什么?
A:總線不同、匹配機制不同、資源獲取不同。virtio 無真實硬件,匹配依賴 virtio_device_id。
Q2:virtqueue 相當于 platform 驅動中的什么?
A:類似于 platform 驅動中通過 ioremap
得到的寄存器,但更加抽象,是“通用的數據傳輸管道”。
Q3:是否可以將 virtio_driver 看成虛擬平臺驅動?
A:從使用方式看類似,但從總線層來看,是完全不同的子系統。
小結
本篇我們通過對 virtio_blk
的深入剖析,完整講解了虛擬總線驅動模型在 Linux 內核中的真實落地。它不是一個“子集”或“附屬”模型,而是一個獨立存在、擁有自己總線匹配機制與通信方式的驅動模型。
下一篇(Day 12),我們將繼續拓展——深入理解 virtio_console
、virtio_net
等設備的實現方式,幫助你逐步掌握虛擬設備開發的核心技能。
如有任何問題,歡迎在評論區留言討論!