驱动开发硬核特训 · Day 11(下篇):从 virtio_blk 看虚拟总线驱动模型的真实落地

🔍

B站相应的视屏教程

📌 内核:博文+视频 - 总线驱动模型实战全解析

敬请关注,记得标为原始粉丝。


🔧

在上篇中,我们已经从理论视角分析了"虚拟总线驱动模型"在 Linux 驱动体系中的独特定位。本篇我们聚焦实战:深入分析一个真实的内核子系统 ------ virtio_blk 虚拟块设备驱动,完整讲清虚拟总线模型的运行机制、设备匹配、驱动注册、驱动结构体组织方式、probe 流程、VQ(virtqueue)使用方式等。我们将对照 platform_driver、i2c_driver,总结异同点,帮助你彻底理解这个典型的虚拟驱动模型。


1. virtio_blk 简介与实战目标

virtio_blk 是 Linux 内核中 VirtIO 虚拟设备框架的一部分,模拟了一个块设备(类似硬盘),用于 KVM 虚拟机中的磁盘访问、容器虚拟块设备等场景。它不对应具体硬件,但提供了完整的设备模型。

我们实战目标是:

  • 看懂 virtio_blk 驱动如何注册。
  • 理解它是如何匹配"虚拟设备"的。
  • 弄清 virtio 总线下设备与驱动之间的绑定机制。
  • 对比 platform_drivervirtio_driver,理解它们的核心区别。

2. virtio 驱动注册机制总览

drivers/block/virtio_blk.c 中,驱动最终通过以下结构注册:

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,
};

通过:

c 复制代码
module_virtio_driver(virtio_blk);

内核完成注册,最终宏会展开为 module_init()module_exit() 自动注册。

这非常类似于 platform_driver 的注册过程:

c 复制代码
module_platform_driver(my_platform_driver);

区别在于总线类型不同:

  • platform_driver 注册到 platform 总线上,匹配 platform_device
  • virtio_driver 注册到 virtio 总线上,匹配 virtio_device

3. virtio 设备和驱动匹配机制

virtio 的匹配方式不基于设备树(也可以配合使用),而是通过 virtio_device_id 表完成。

c 复制代码
static const struct virtio_device_id id_table[] = {
    { VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID },
    { 0 },
};
  • VIRTIO_ID_BLOCK 是预定义的设备类型标识,代表块设备。
  • 系统在虚拟机启动时(比如 QEMU)会注入 virtio_device,并调用驱动的 probe

匹配过程:

text 复制代码
virtio_bus.c
  → virtio_register_driver()
    → __register_driver()
      → bus_add_driver()
        → driver_match_device() // 比较 virtio_id

platform_driverof_match_table 匹配方式略有不同,virtio_driver 更偏向于"协议栈类型匹配"。


4. 重点函数分析:virtblk_probe()

c 复制代码
static int virtblk_probe(struct virtio_device *vdev) {
    // 关键:分配 virtio_blk 结构体,挂载到 vdev->priv
    vdev->priv = vblk = kmalloc(...);

    // 初始化 virtqueue
    init_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 是怎样注册块设备的?

从注册 gendiskblk_mq 调度器配置:

c 复制代码
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 实现:

c 复制代码
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_consolevirtio_net 等设备的实现方式,帮助你逐步掌握虚拟设备开发的核心技能。

如有任何问题,欢迎在评论区留言讨论!

相关推荐
独行soc16 分钟前
2025年渗透测试面试题总结-某服面试经验分享(附回答)(题目+回答)
linux·运维·服务器·网络安全·面试·职场和发展·渗透测试
sunshineine1 小时前
jupyter notebook运行简单程序
linux·windows·python
O。o.尊都假都1 小时前
UDP协议
linux·服务器·网络·网络协议·udp
天夏已微凉2 小时前
1.3.1 Linux音频框架alsa详细介绍
linux·音视频
惜.己2 小时前
linux中的常用命令(一)
linux·运维·服务器
教练、我想打篮球4 小时前
04 基于 STM32 的时钟展示程序
stm32·单片机·嵌入式硬件
m0_593758104 小时前
系统重装之后,通过ssh无法登录
linux·运维·服务器
银河麒麟操作系统4 小时前
【银河麒麟高级服务器操作系统】服务器外挂存储ioerror分析及处理分享
linux·运维·服务器
某不知名網友4 小时前
Linux_进程退出与进程等待
linux·运维·服务器
sukalot4 小时前
window 显示驱动开发-用户模式显示驱动程序的线程模型
驱动开发