当前位置: 首页 > news >正文

linux 之virtio 的驱动框架

1、基本知识

上一篇文章介绍了 virtio 的核心数据的实现和逻辑:linux 之 virtio 子系统核心的数据结构-CSDN博客

virtio 是对半虚拟化 hypervisor 中的一组通用模拟设备的抽象。它允许 hypervisor 导出一组通用的模拟设备,并通过一个通用的应用编程接口(API)让它们变得可用。下图展示了为什么这很重要。有了半虚拟化 hypervisor 之后,客户操作系统能够实现一组通用的接口,在一组后端驱动程序之后采用特定的设备模拟。后端驱动程序不需要是通用的,因为它们只实现前端所需的行为。

注意,在现实中(尽管不需要),设备模拟发生在使用 QEMU 的空间,因此后端驱动程序与 hypervisor 的用户空间交互,以通过 QEMU 为 I/O 提供便利。QEMU 是一个系统模拟器,它不仅提供客户操作系统虚拟化平台,还提供整个系统(PCI 主机控制器、磁盘、网络、视频硬件、USB 控制器和其他硬件元素)的模拟。

2、virtio的模块层次

2.1 virtio 的驱动的框架

除了前端驱动(在客户机操作系统中实现)和后端驱动(在虚拟机 hypervisor 中实现)之外,virtio 还定义了 2 个层次来支持客户机到 hypervisor 的通信。

在顶层(称为 virtio 层)是虚拟队列接口,它在概念上将前端驱动程序附加到后端驱动,驱动可以根据需要使用零个或多个队列。例如,virtio 网络驱动使用两个虚拟队列(一个用于接收,一个用于发送),而 virtio 块驱动只使用一个队列。虚拟队列是虚拟的,它通过 ring 实现,以用于遍历客户机到 hypervisor 的信息。这可以以任何方式来实现,只要 Guest 和 hypervisor 通过相同的方式来实现。

内核中实现的virtio 的前端驱动框架:

如图 所示,列出了五个前端驱动程序,分别用于块设备(例如磁盘)、网络设备、PCI 模拟、balloon 驱动(用于动态管理客户机内存使用)和控制台驱动。每个前端驱动在 hypervisor 中都有一个对应的后端驱动。

2.2 在驱动中, 前端驱动程序肯定是注册了以下数据结构的关系

virtio 前端对象层次结构:

从客户操作系统的角度来看,对象层次结构 的定义如上图所示。在顶级的是 virtio_driver,它在客户操作系统中表示前端驱动程序。与该驱动程序匹配的设备由 virtio_device(设备在客户操作系统中的表示)封装。这引用 virtio_config_ops 结构(它定义配置 virtio 设备的操作)。virtio_device 由 virtqueue 引用(它包含一个到它服务的 virtio_device 的引用)。最后,每个 virtqueue 对象引用 virtqueue_ops 对象,后者定义处理 hypervisor 的驱动程序的底层队列操作。

virtio_driver 结构定义上层设备驱动程序、驱动程序支持的设备 ID 的列表、一个特性表单(取决于设备类型)和一个回调函数列表。当 hypervisor 识别到与设备列表中的设备 ID 相匹配的新设备时,将调用 probe 函数(由 virtio_driver 对象提供)来传入 virtio_device 对象。将这个对象和设备的管理数据缓存起来(以独立于驱动程序的方式缓存)。可能要调用 virtio_config_ops 函数来获取或设置特定于设备的选项,例如,为 virtio_blk 设备获取磁盘的 Read/Write 状态或设置块设备的块大小,具体情况取决于启动器的类型。

virtio_device 不包含到 virtqueue 的引用(但 virtqueue 确实引用了 virtio_device)。要识别与该 virtio_device 相关联的 virtqueue,需要结合使用 virtio_config_ops 对象和 find_vq 函数。该对象返回与这个 virtio_device 实例相关联的虚拟队列。find_vq 函数还允许为 virtqueue 指定一个回调函数(查看左 图中的 virtqueue 结构)。

virtqueue 是一个简单的结构,它识别一个可选的回调函数(在 hypervisor 使用缓冲池时调用)、一个到 virtio_device 的引用、一个到 virtqueue 操作的引用,以及一个引用要使用的底层实现的特殊 priv 引用。虽然 callback 是可选的,但是它能够动态地启用或禁用回调。

virtio缓冲池:客户操作系统(前端)驱动程序通过缓冲池与 hypervisor 交互。对于 I/O,客户操作系统提供一个或多个表示请求的缓冲池。例如,你可以提供 3 个缓冲池,第一个表示 Read 请求,后面两个表示响应数据。该配置在内部被表示为一个散集列表(scatter-gather),列表中的每个条目表示一个地址和一个长度。缓冲区的格式、顺序和内容仅对前端和后端驱动程序有意义。内部传输(当前实现中的连接点)仅移动缓冲区,并且不知道它们的内部表示。

该层次结构的核心是 virtqueue_ops,它定义在客户操作系统和 hypervisor 之间移动命令和数据的方式:

第一个函数 add_buf 用于向 hypervisor 提供请求。如前面所述,该请求以散集列表的形式存在。对于 add_buf,客户操作系统提供用于将请求添加到队列的 virtqueue、散集列表(地址和长度数组)、用作输出条目(目标是底层 hypervisor)的缓冲池数量,以及用作输入条目(hypervisor 将为它们储存数据并返回到客户操作系统)的缓冲池数量。当通过 add_buf 向 hypervisor 发出请求时,客户操作系统能够通过 kick 函数通知 hypervisor 新的请求。为了获得最佳的性能,客户操作系统应该在通过 kick 发出通知之前将尽可能多的缓冲池装载到 virtqueue。

第二个函数get_buf 处理自 hypervisor 的响应。客户操作系统仅需调用该函数或通过提供的 virtqueue callback 函数等待通知就可以实现轮询。当客户操作系统知道缓冲区可用时,调用 get_buf 返回完成的缓冲区。

最后两个函数enable_cb 和 disable_cb用来启用或禁用回调进程(通过在 virtqueue 中由 virtqueue 初始化的 callback 函数)。注意,该回调函数和 hypervisor 位于独立的地址空间中,因此调用通过一个间接的 hypervisor 来触发(比如 kvm_hypercall)。

3、virtio  的驱动实例, 网友写的比较清楚的virtio-net 的前端驱动程序

Linux virtio-net driver - 知乎

后面找个时间具有看一下源码。

4、virtio  的后端程序实例

一定要结合一个后端驱动进行分析,可以对照rpmsg-lite分析

http://www.dtcms.com/a/343388.html

相关文章:

  • Motocycle 智能仪表盘
  • 白光干涉测量系统的复合相移三维重建和多视场形貌拼接的复现
  • 【自然语言处理与大模型】微调与RAG的区别
  • JavaScript基础语法five
  • 【Protues仿真】基于AT89C52单片机的数码管驱动事例
  • 力扣905:按奇偶排序数组
  • 2025-08-21 Python进阶4——错误和异常
  • 开发者中使用——控制台打印数据
  • 爬虫基础学习-基本原理和GET请求
  • JavaScript 基本语法
  • 智慧城市SaaS平台/市政设施运行监测系统之空气质量监测系统、VOC气体监测系统、污水水质监测系统及环卫车辆定位调度系统架构内容
  • 学习嵌入式之驱动
  • 3.2.6 混凝土基础施工
  • Chrome 内置扩展 vs WebUI:浏览器内核开发中的选择与实践
  • C++入门自学Day16-- STL容器类型总结
  • Git标准化开发流程
  • iOS 应用上架多环境实战,Windows、Linux 与 Mac 的不同路径
  • 详解开源关键信息提取方案PP-ChatOCRv4的设计与实现
  • 哈尔滨云前沿服务器租用类型
  • IoTDB如何解决海量数据存储难题?
  • 多模态大模型研究每日简报【2025-08-21】
  • Python学习-- 数据库和MySQL入门
  • 在线课程|基于SprinBoot+vue的在线课程管理系统(源码+数据库+文档)
  • 华为仓颉语言的函数初步
  • rust语言 (1.88) egui (0.32.1) 学习笔记(逐行注释)(三) 文本标签、按钮、文本框基本使用
  • Rust 入门 模块 (二十二)
  • 意象驱动下的感知与认知信息结构:上古汉语同源词研究视角——基于黄易青《上古汉语同源词意义系统研究》的理论框架
  • Eject配置
  • 常见的端口扫描
  • 从零到一:RAGFlow 本地部署全攻略