极客油画

数据流方向:virtio前端驱动 -> virtio -> transport(virtio-ring) -> virtio后端驱动

virtio前端驱动:

  • 运行在虚拟机中
  • 针对不同类型的设备有不同的驱动程序,但是与后端驱动交互的接口都是统一的
  • virtio_net模块,源码位于 drivers/net/virtio_net.c
  • virtio_blk,virtio_net,virtio_pci,virtio_ballon,virtio_scsi

virtio层:

  • virtio层实现虚拟队列接口,作为前后端通信的桥梁
  • 不同类型的设备使用的虚拟队列数量不同,比如virtio_net使用两个队列,一个用于接收,另一个用于发送
  • 源码位于 drivers/virtio/virtio.c

virtio-ring层:

  • virtio-ring层是虚拟队列的具体实现
  • 源码位于 driver/virtio/virtio_ring.c

virtio后端驱动

  • 运行在宿主机
  • 实现virtio后端的逻辑,主要是操作硬件设备,比如向内核协议栈发送一个网络包完成虚拟机对于网络的操作。
  • 在Qemu+KVM虚拟化环境中,virtio后端驱动的代码位于Qemu源码中。

虚拟机中运行的virtio驱动前端是否需要手动安装

并非所有操作系统都默认支持VirtIO设备。例如,Windows操作系统不自带对VirtIO设备的支持,因此必须手动安装对应的驱动程序。

windows虚拟机安装virtio前端驱动的步骤:

  • 添加临时VirtIO设备:为了能够从VirtIO驱动ISO中安装驱动程序,你可能需要先向虚拟机添加一个临时的VirtIO设备(比如一个空的VirtIO SCSI控制器)。这允许你在没有完全切换到VirtIO之前就能够访问驱动程序。
  • 加载VirtIO ISO:下载最新的VirtIO驱动ISO并将它作为CD-ROM挂载到虚拟机中。
  • 安装驱动程序:进入Windows后,在设备管理器中找到未识别或带有黄色感叹号的设备(前面添加的临时VirtIO设备),然后选择更新驱动程序软件,并指向VirtIO ISO所在的虚拟光驱进行安装。
  • 重启虚拟机:完成驱动安装后,关闭虚拟机,并将虚拟硬盘重新配置为使用VirtIO SCSI控制器而不是SATA控制器,之后再次启动虚拟机以验证是否正确识别了新的控制器。

linux虚拟机通常自带了virtio前端驱动,确认virtio驱动是否加载的方法:

  • lsmod | grep virtio:如果输出结果中有类似virtio_net, virtio_blk, 或者其他的VirtIO模块,那么说明这些驱动已经被加载并正在使用。
  • 检查初始化RAM磁盘(initrd): 有时即使内核支持VirtIO设备,但如果初始化RAM磁盘(initrd)没有包含必要的模块,系统在启动时可能无法正确识别这些设备。
    • 编辑/etc/sysconfig/kernel文件,并添加或确保以下行存在:INITRD_MODULES="virtio_blk virtio_net"
    • 然后重建initrd镜像:dracut -f

virtio后端

virtio后端代码位于QEMU源码中(如hw/virtio/目录),负责模拟PCI设备(如virtio-net、virtio-blk),并通过vring与客户机前端驱动交互。

工作流程:

  1. 初始化:
    • PCI设备探测时,QEMU注册virtio设备(如virtio_blk_init_pci),初始化vring和回调函数
    • 客户机启动后,前后端通过特性协商(feature bits)确定支持的功能
  2. 请求处理:
    • 前端驱动将I/O请求描述符写入vring的Available Ring,并通知后端(通过I/O端口或MMIO)
    • 后端从vring读取请求,解析VirtQueueElement结构,映射客户机物理地址到宿主机虚拟地址,执行实际I/O(如写文件、发送网络包)
    • 完成操作后,后端将结果写入Used Ring,并注入中断通知前端

virtio 和 QEMU Object Model

在QEMU中,Virtio设备的初始化过程基于QOM(QEMU Object Model)框架实现,主要涉及设备类型注册、类初始化、实例创建及Realize阶段。以下是关键流程分析:

  1. 设备类型注册

通过type_init宏注册设备类型,例如Virtio-Net-PCI设备:

1
type_init(virtio_net_pci_register)

此阶段会调用type_register_static将设备类型信息(如TypeInfo)注册到QOM系统中,包括类初始化函数(class_init)和实例初始化函数(instance_init)

  1. 类初始化(class_init)

    • 继承关系:Virtio设备通常继承自PCI设备(如TYPE_VIRTIO_PCI)和基础设备类(TYPE_DEVICE)。类初始化时,会依次调用父类的class_init,再设置子类的特定回调(如realize函数)
    • 示例:virtio_net_pci_class_init会设置realize为virtio_net_pci_realize,并初始化Virtio总线相关属性
  2. 实例创建与初始化

    • 对象创建:通过object_new创建设备实例,触发instance_init函数初始化对象属性(如设置PCI配置空间)
    • 依赖注入:例如Virtio-Net设备会关联Tap后端设备,通过peer字段建立前后端连接
  3. Realize阶段

调用object_property_set_bool(…, “realized”, true)触发设备实现:

* PCI代理设备Realize:virtio_pci_realize初始化PCI配置,注册内存区域和中断
* Virtio设备Realize:virtio_device_realize通用初始化(如设置设备ID、Features),并调用设备特定的realize函数(如virtio_net_device_realize)创建VirtQueue和NetClientState
* 总线绑定:virtio_bus_device_plugged将设备插入Virtio总线,通知Guest OS驱动加载
  1. VirtQueue初始化

通过virtio_add_queue创建VirtQueue,并设置回调函数(如virtio_net_handle_rx)。Guest驱动通过PCI配置空间传递VirtQueue地址,QEMU注册eventfd实现异步通知

关键数据结构:

* VirtIODevice:所有Virtio设备的基类,包含设备ID、VirtQueue数组等
* VirtQueue:核心通信机制,包含VRing、事件通知回调等

创建Virtio-Net设备的命令:

1
-device virtio-net-pci,netdev=tap0

QEMU解析后依次执行上述流程,最终形成前后端通信的完整设备,整个过程体现了QOM的面向对象设计,通过类继承、多态和属性绑定实现灵活的设备模型。


本文发表于 0001-01-01,最后修改于 0001-01-01。

本站永久域名「 jiavvc.top 」,也可搜索「 极客油画 」找到我。


上一篇 « 下一篇 »

赞赏支持

请我吃鸡腿 =^_^=

i ysf

云闪付

i wechat

微信

推荐阅读

Big Image