Virtio 作为 Linux 虚拟化中前后端通信的核心标准,前文Linux Virtio 驱动框架:架构设计与实现解析解析了整个virtio框架,本文将聚焦 Virtio 子系统的六大核心数据结构,结合代码实现与内存布局,拆解其设计逻辑与核心作用。
1. virtio_bus:虚拟设备的总线基石
virtio_bus是基于 Linux 总线 - 驱动 - 设备模型的虚拟总线,负责管理 virtio 设备与驱动的匹配与生命周期,定义于drivers/virtio/virtio.c。
cpp
static struct bus_type virtio_bus = {
.name = "virtio",
.match = virtio_dev_match,
.dev_groups = virtio_dev_groups,
.uevent = virtio_uevent,
.probe = virtio_dev_probe,
.remove = virtio_dev_remove,
};
-
注册机制 :通过
core_initcall(virtio_init)注册,启动优先级远高于普通模块(module_init对应device_initcall),确保虚拟化环境早期就绪。 -
匹配逻辑 :
virtio_dev_match函数通过virtio_device_id匹配设备与驱动,需同时满足device和vendor字段,且驱动的id_table必须以device=0结尾终止循环。 -
核心作用 :作为设备与驱动的 "中间人",完成驱动绑定(
probe)与解绑(remove)的触发。
2. virtio_device:虚拟设备的身份与配置载体
virtio_device封装了虚拟设备的核心属性与状态,定义于include/linux/virtio.h,是前端设备在内核中的抽象表示。
cpp
/**
* struct virtio_device - Virtio设备核心结构体,封装虚拟设备的属性、状态与操作接口
* @index: 设备索引,用于区分同一virtio总线上的多个设备实例(如多块virtio-net网卡)
* @failed: 设备故障标记,标识设备是否初始化/运行过程中出现错误,快速判断设备健康状态
* @config_core_enabled: 配置核心功能启用标记,内核侧是否允许访问/修改设备配置空间
* @config_driver_disabled: 驱动侧配置禁用标记,驱动主动禁用设备配置功能(用于管控配置修改场景)
* @config_change_pending: 配置变更待处理标记,标识设备配置项已变更但未完成处理,防止并发处理配置变更
* @config_lock: 配置操作自旋锁,保护配置相关字段(如features、config_change_pending)的并发读写
* @vqs_list_lock: 虚拟队列链表自旋锁,保护vqs链表的增删/遍历操作(防止多线程操作队列链表竞态)
* @dev: 内核通用设备结构体,将virtio设备接入Linux设备模型,提供总线匹配、电源管理、设备生命周期等通用能力
* @id: Virtio设备ID结构体(含vendor ID和device ID),是驱动与设备匹配的核心标识(如VIRTIO_ID_NET=1表示网卡)
* @config: 配置操作接口集指针,指向设备专属的配置操作函数表(如读写配置空间、设置设备状态、查找虚拟队列等)
* @vringh_config: vringh(虚拟环主机侧)配置操作接口,用于主机侧对vring(虚拟环)的配置与管理
* @vqs: 虚拟队列(virtqueue)链表头,挂载该设备关联的所有virtqueue实例(如收/发队列),统一管理数据传输队列
* @features: 前后端协商后的特性集,64位位图标识双方均支持的Virtio特性(如间接描述符、事件索引等)
* @priv: 私有数据指针,驱动侧存放设备专属上下文(如硬件寄存器地址、自定义状态),增强结构体扩展性
* @debugfs_dir: debugfs目录项,仅启用CONFIG_VIRTIO_DEBUG时有效,指向设备专属的debugfs目录(暴露调试信息)
* @debugfs_filter_features: debugfs特性过滤掩码,仅启用CONFIG_VIRTIO_DEBUG时有效,用于过滤展示指定的Virtio特性
*/
struct virtio_device {
int index; // 设备索引,区分同总线的多个virtio设备实例
bool failed; // 设备故障标记:true表示设备初始化/运行失败
bool config_core_enabled; // 配置核心功能启用标记:true表示内核启用配置功能
bool config_driver_disabled; // 驱动侧配置禁用标记:true表示驱动禁用配置修改
bool config_change_pending; // 配置变更待处理标记:true表示有配置变更未处理
spinlock_t config_lock; // 配置操作自旋锁,保护配置相关字段并发访问
spinlock_t vqs_list_lock; // 虚拟队列链表自旋锁,保护vqs链表并发操作
struct device dev; // 内核通用设备对象,接入Linux设备模型
struct virtio_device_id id; // Virtio设备ID,用于驱动-设备匹配
const struct virtio_config_ops *config; // 配置操作接口集,设备专属配置函数表
const struct vringh_config_ops *vringh_config; // vringh配置操作接口
struct list_head vqs; // 虚拟队列链表头,挂载该设备的所有virtqueue
u64 features; // 协商后的特性集,64位位图表示支持的Virtio特性
void *priv; // 私有数据指针,驱动侧存放设备专属上下文
#ifdef CONFIG_VIRTIO_DEBUG
struct dentry *debugfs_dir; // debugfs目录项,设备专属调试目录
u64 debugfs_filter_features; // debugfs特性过滤掩码,过滤展示指定特性
#endif
};
-
virtio_device_id :设备功能标识,如
VIRTIO_ID_NET=1(网络设备)、VIRTIO_ID_BLOCK=2(块设备),支持VIRTIO_DEV_ANY_ID=0xffffffff通配匹配。 -
virtio_config_ops :配置操作接口,含设备属性读写(
get/set)、状态管理(get_status/set_status)、虚拟队列实例化(find_vqs)等核心操作。 -
vqs 链表 :挂载该设备对应的所有
virtqueue,如 virtio-net 默认包含接收和发送两个队列。 -
features:存储前后端协商一致的通信特性,是 Virtio 功能扩展的核心载体。
3. virtio_driver:虚拟设备的驱动实现
virtio_driver定义了虚拟设备的驱动逻辑,与virtio_device通过virtio_bus完成匹配,定义于include/linux/virtio.h。
cpp
struct virtio_driver {
struct device_driver driver;
const struct virtio_device_id *id_table; // 支持的设备ID列表
unsigned int feature_table_size_legacy;
const unsigned int *feature_table; // 支持的特性列表
unsigned int feature_table_size;
const unsigned int *feature_table_legacy;
int (*probe)(struct virtio_device *dev); // 设备绑定回调
#ifdef CONFIG_PM
int (*freeze)(struct virtio_device *dev);
int (*restore)(struct virtio_device *dev);
#endif
void (*scan)(struct virtio_device *dev);
void (*remove)(struct virtio_device *dev); // 设备解绑回调
void (*config_changed)(struct virtio_device *dev); // 配置变更回调
};
-
通过
id_table声明支持的设备类型,通过feature_table声明支持的通信特性。 -
实现
probe回调完成设备初始化,remove回调释放资源,是驱动逻辑的核心入口。
4. virtqueue:前后端通信的队列抽象
virtqueue是 Virtio 子系统的通信核心,作为前端与后端(Guest 与 Host)之间的缓冲队列,负责数据传输的调度与管理,定义于include/linux/virtio.h。
cpp
struct virtqueue {
struct list_head list; // 接入virtio_device的vqs链表
void (*callback)(struct virtqueue *vq); // 队列触发回调
const char *name; // 队列名称
struct virtio_device *vdev; // 关联的虚拟设备
unsigned int index; // 队列编号
unsigned int num_free; // 空闲描述符数量
void *priv; // 私有数据(通常指向vring_virtqueue)
};
- 是数据传输的 "通道",每个队列对应一类 IO 操作(如接收、发送)。
- 通过
callback函数响应队列事件(如数据到达、发送完成),是中断处理的核心入口。 - 底层依赖
vring结构实现具体的内存布局与数据传输。
5. vring:虚拟队列的内存布局实现
vring(Virtual Ring)是virtqueue的底层内存布局实现,定义于include/uapi/linux/virtio_ring.h,规范了 Guest 与 Host 共享内存的使用方式。
vring由三个核心区域组成,存储于 Guest 与 Host 共享的连续内存中:
-
Descriptor Table(描述符表):存储 buffer 的物理地址(GPA)、长度、标志及下一个描述符索引,支持链式结构。
-
Avail Ring(可用环):由 Guest 写入,记录可供 Host 读取的描述符索引,Host 从中获取待处理的 IO 请求。
-
Used Ring(已用环):由 Host 写入,记录已处理完成的描述符索引及实际传输长度,Guest 从中获取处理结果。
cpp
// 描述符表项
struct vring_desc {
__virtio64 addr; // 缓冲区物理地址
__virtio32 len; // 缓冲区长度
__virtio16 flags; // 描述符属性(如可读、可写、链式)
__virtio16 next; // 下一个描述符索引
};
// 可用环
struct vring_avail {
__virtio16 flags; // 控制标志(如禁用中断)
__virtio16 idx; // 下一个可用描述符索引
__virtio16 ring[]; // 可用描述符索引列表
__virtio16 used_event; // 中断触发阈值(流控机制)
};
// 已用环项
struct vring_used_elem {
__virtio32 id; // 已处理描述符索引
__virtio32 len; // 实际传输长度
};
// 已用环
struct vring_used {
__virtio16 flags; // 控制标志
__virtio16 idx; // 下一个已用描述符索引
struct vring_used_elem ring[]; // 已用描述符列表
__virtio16 avail_event; // 中断触发阈值(流控机制)
};
// 完整vring结构
struct vring {
unsigned int num; // 描述符数量
struct vring_desc *desc; // 描述符表指针
struct vring_avail *avail; // 可用环指针
struct vring_used *used; // 已用环指针
};
-
内存布局 :通过
vring_init函数初始化,三个区域在共享内存中连续排布,used区域需按指定对齐要求向上对齐。 -
大小计算 :通过
vring_size函数计算实际占用内存,包含描述符表、可用环、已用环及对齐填充空间。 -
中断流控 :
used_event与avail_event字段控制中断触发阈值,避免频繁中断切换导致的性能损耗。
6.vring_virtqueue:前端队列的具体实现
vring_virtqueue是virtqueue的具体实现,封装了vring的操作细节,是前端驱动中直接使用的队列结构。
cpp
struct vring_virtqueue {
struct virtqueue vq; // 基类virtqueue
struct vring vring; // 底层vring结构
bool weak_barriers; // 是否支持弱内存屏障
bool broken; // 后端状态异常标记
bool indirect; // 是否支持间接描述符
bool event; // 是否支持事件流控
unsigned int free_head; // 空闲描述符链表头
unsigned int num_added; // 上次同步后新增请求数
u16 last_used_idx; // 上次读取的已用环索引
u16 avail_flags_shadow; // 可用环标志缓存
u16 avail_idx_shadow; // 可用环索引缓存
bool (*notify)(struct virtqueue *vq); // 通知后端函数
bool we_own_ring; // 环内存所有权标记
size_t queue_size_in_bytes; // 队列内存大小
dma_addr_t queue_dma_addr; // 队列DMA地址
#ifdef DEBUG
unsigned int in_use;
bool last_add_time_valid;
ktime_t last_add_time;
#endif
struct vring_desc_state desc_state[]; // 描述符状态数组
};
- 桥接
virtqueue的抽象接口与vring的底层实现,隐藏内存操作细节。 - 维护描述符的空闲链表(
free_head),高效管理缓冲区资源。 - 通过
notify函数触发后端中断,完成前后端通信的同步。
7.核心结构关系总结
-
层级关系 :
virtio_bus管理virtio_device与virtio_driver的匹配,virtio_device通过vqs链表关联多个virtqueue,virtqueue由vring_virtqueue实现并依赖vring完成内存通信。 -
数据流向 :Guest 通过
virtqueue提交 IO 请求,经vring的共享内存传递给 Host,Host 处理后通过vring反馈结果,最终由virtqueue的callback函数通知前端驱动。
理解这些核心数据结构的设计逻辑,是掌握 Virtio 虚拟化通信机制、进行驱动开发与性能优化的关键基础。