🔍
B站相应的视屏教程:
📌 内核:博文+视频 - 总线驱动模型实战全解析
敬请关注,记得标为原始粉丝。
🔧
在 Linux 内核的驱动模型体系中,虚拟总线驱动模型常常因“无硬件”而被忽略,却又在现代虚拟化、模拟设备、模块内通信中发挥着至关重要的作用。它不仅是一种总线机制,更是内核中“驱动模型思想”与“运行时绑定策略”的典型代表。
本文将围绕以下四个核心维度展开:
- 什么是虚拟总线驱动模型?
- 它与平台驱动、I2C/SPI 总线驱动有何不同?
- 它的结构与使用方式有何特点?
- 实际案例剖析:以 virtio-blk 驱动为核心代码,讲解其如何体现“虚拟总线”机制。
一、驱动模型中的虚拟总线是什么?
在 Linux 中,驱动模型基于“设备(device)- 驱动(driver)- 总线(bus)”的三元关系运作。每个 device 和 driver 都必须挂载到某种 bus 上,由总线完成它们的“匹配”和“绑定”。
通常我们熟悉的总线包括:
- I2C 总线(
i2c_driver
,i2c_client
) - SPI 总线(
spi_driver
,spi_device
) - PCI 总线(
pci_driver
,pci_device
) - 平台总线(
platform_driver
,platform_device
)
而虚拟总线(virtual bus)是一种不依赖真实硬件接口的逻辑抽象,用于管理和匹配“逻辑上的设备与驱动”。
定义特点:
- 不依赖真实物理硬件:比如 virtio、vhost、rpmsg 等驱动,都是在软件层实现的“虚拟硬件”,不通过 GPIO/I2C/SPI 接口通信。
- 设备和驱动的注册通常通过软件创建,如
virtio_register_driver()
、device_register()
。 - 匹配过程依赖于 name/ID,而不是物理探测或者 probe 地址。
- 常用于虚拟机、模拟设备、通信桥梁、文件系统内设备等场景。
二、虚拟总线 ≠ 平台总线
有观点认为“虚拟设备使用 platform_driver 注册,也是一种虚拟总线”。这在一些情况下成立,比如:
platform_device_register(&virtual_device);
但本质上:
特性 | 虚拟总线驱动(virtio等) | 平台驱动模型 |
---|---|---|
总线结构 | 自定义 bus_type (如 virtio_bus) |
使用统一的 platform_bus_type |
匹配方式 | 通常使用 .id_table 或 .name |
通过 of_match_table 或 name |
用途 | 虚拟化、内核模块通信等 | 系统级通用外设(真实或虚拟) |
是否专属硬件接口 | 否,纯软件逻辑实现 | 是,逻辑上绑定 SoC 资源 |
结论:虚拟总线驱动是一种独立的驱动模型分类,并不属于 platform_driver 的子集。尽管它们都支持无硬件匹配,但模型设计初衷和系统层级不同。
三、结构解析:虚拟总线驱动的典型框架
以 virtio
框架为代表,虚拟总线驱动模型通常包括以下结构:
1. 定义一个虚拟总线(bus_type
)
struct bus_type virtio_bus = {
.name = "virtio",
.match = virtio_device_match,
.probe = virtio_device_probe,
.remove = virtio_device_remove,
// ...
};
其中:
.match()
决定设备和驱动如何匹配.probe()
驱动初始化时调用.remove()
卸载时清理资源
2. 注册一个虚拟设备(struct virtio_device
)
virtio_device_register(vdev);
通常在底层虚拟机框架、前端驱动(如 vhost、qemu、virtio-mmio)中调用。
3. 注册一个虚拟驱动(virtio_driver
)
static struct virtio_driver virtio_blk = {
.driver.name = "virtio-blk",
.id_table = id_table,
.probe = virtblk_probe,
.remove = virtblk_remove,
};
module_virtio_driver(virtio_blk);
id_table
用于匹配probe()
中注册 block 设备、初始化 virtqueue
4. 匹配机制(run-time binding)
设备与驱动的匹配过程由 .id_table
和 .match()
共同决定,通常基于 virtio_id
。
四、核心优势与关键概念
特性 | 描述 |
---|---|
抽象性高 | 无需真实硬件,适合模拟设备、容器通信、用户态桥接等 |
内核模块通信机制 | 如 rpmsg 驱动、virtio 控制台等都使用虚拟总线构建了模块间的通信桥 |
启动速度快、兼容性好 | 虚拟设备无需依赖物理探测与 IRQ 绑定,初始化更快 |
更适合复杂的软件架构 | 比如在 KVM/QEMU 中提供虚拟设备,Guest 端使用 virtio 驱动访问 |
五、实际代码举例(精简版)
以 virtio_blk
为例:
static const struct virtio_device_id id_table[] = {
{ VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID },
{ 0 },
};
static struct virtio_driver virtio_blk = {
.driver.name = "virtio-blk",
.id_table = id_table,
.probe = virtblk_probe,
.remove = virtblk_remove,
};
module_virtio_driver(virtio_blk);
注册总线的代码:
bus_register(&virtio_bus); // 在 init 函数中注册
注册虚拟设备的调用:
device_register(&vdev->dev);
bus_add_device(&virtio_bus, &vdev->dev);
匹配调用链:
driver_register() → bus_add_driver() → match() → driver->probe()
六、小结与回顾
虚拟总线驱动模型是 Linux 驱动模型体系中的一颗“明珠”:
- 它代表了纯软件抽象驱动匹配机制;
- 是虚拟化、模拟驱动、模块通信的核心机制;
- 与 platform_driver 虽结构相似,但目的和范畴不同;
- 是设备模型理念中的“运行时适配”机制的典范。
七、预告下篇内容
下篇我们将脱离 virtio 框架,采用更贴近“纯虚拟驱动模型”的方式,在 Linux 内核中模拟创建一个“虚拟设备 + 虚拟驱动”的完整结构,用代码一步步解释设备模型如何支持这种“无硬件运行时适配”。
如果你觉得“虚拟总线驱动”只是内核开发中的“边角料”,那么这篇文章希望让你看到它背后隐藏的真正价值。
Day 11(下篇)即将到来:“纯虚拟设备模型实战开发”!