Linux Virtio 子系统核心数据结构解析

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匹配设备与驱动,需同时满足devicevendor字段,且驱动的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
};
  1. virtio_device_id :设备功能标识,如VIRTIO_ID_NET=1(网络设备)、VIRTIO_ID_BLOCK=2(块设备),支持VIRTIO_DEV_ANY_ID=0xffffffff通配匹配。

  2. virtio_config_ops :配置操作接口,含设备属性读写(get/set)、状态管理(get_status/set_status)、虚拟队列实例化(find_vqs)等核心操作。

  3. vqs 链表 :挂载该设备对应的所有virtqueue,如 virtio-net 默认包含接收和发送两个队列。

  4. 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 共享的连续内存中:

  1. Descriptor Table(描述符表):存储 buffer 的物理地址(GPA)、长度、标志及下一个描述符索引,支持链式结构。

  2. Avail Ring(可用环):由 Guest 写入,记录可供 Host 读取的描述符索引,Host 从中获取待处理的 IO 请求。

  3. 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_eventavail_event字段控制中断触发阈值,避免频繁中断切换导致的性能损耗。

6.vring_virtqueue:前端队列的具体实现

vring_virtqueuevirtqueue的具体实现,封装了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.核心结构关系总结

  1. 层级关系virtio_bus管理virtio_devicevirtio_driver的匹配,virtio_device通过vqs链表关联多个virtqueuevirtqueuevring_virtqueue实现并依赖vring完成内存通信。

  2. 数据流向 :Guest 通过virtqueue提交 IO 请求,经vring的共享内存传递给 Host,Host 处理后通过vring反馈结果,最终由virtqueuecallback函数通知前端驱动。

理解这些核心数据结构的设计逻辑,是掌握 Virtio 虚拟化通信机制、进行驱动开发与性能优化的关键基础。

相关推荐
贝塔实验室2 小时前
Altium Designer 6.0 初学教程-如何生成一个集成库并且实现对库的管理
linux·服务器·前端·fpga开发·硬件架构·基带工程·pcb工艺
阿巴~阿巴~2 小时前
TCP服务器实现全流程解析(简易回声服务端):从套接字创建到请求处理
linux·服务器·网络·c++·tcp·socket网络编程
赖small强2 小时前
【Linux C/C++开发】第20章:进程间通信理论
linux·c语言·c++·进程间通信
赖small强2 小时前
【Linux C/C++开发】第24章:现代C++特性(C++17/20)核心概念
linux·c语言·c++·c++17/20
Robpubking3 小时前
elasticsearch 使用 systemd 启动时卡在 starting 状态 解决过程记录
linux·运维·elasticsearch
hlsd#3 小时前
我把自己的小米ax3000t换成了OpenWRT
linux·iot
不想画图3 小时前
Linux——web服务介绍和nginx编译安装
linux·nginx
2301_807583234 小时前
ubuntu22.04集群部署clickhouse详细步骤
linux·clickhouse·zookeeper
尹蓝锐4 小时前
Linux解压各种压缩包命令
linux·运维·服务器