天天看点

聊聊KVM的半虚拟化virtio驱动

作者:云原生驿站

QEMU/KVM提供了全虚拟化环境,可以让客户机不经过任何修改就能运行在KVM环境当中。不过,KVM在I/O虚拟化方面,传统的方式是使用QEMU纯软件的方式来模拟I/O设备,其效率并不高。在KVM中,可以在客户机使用半虚拟化驱动(Paravirtualized Drives,PV Drivers)来提高客户机的性能。目前KVM使用中实现半虚拟化的方式是采用virtio这个Linux上的设备驱动标准框架。

实现IO虚拟化主要有三种方式:全虚拟化、半虚拟化和透传。全虚拟化Guest OS不会感知到自己是虚拟机,也无需修改Guest OS,但是它的效率比较低。半虚拟化Guest OS知道自己是虚拟机,通过Frontend/Backend驱动模拟实现IO虚拟化。透传就是直接分配物理设备给VM用。Virtio是一种半虚拟化的设备抽象接口规范,在Qemu和KVM中得到了广泛使用

QEMU模拟I/O设备的基本原理和优缺点

聊聊KVM的半虚拟化virtio驱动

在使用QEMU模拟I/O的情况下,当客户机中的设备驱动程序(Device Driver)发起I/O操作请求时,KVM模块中的I/O操作捕获代码会拦截这次I/O请求,然后在经过处理后将本地I/O请求的信息存放到I/O共享页(sharing page),并通知用户空间的QEMU程序。QEMU模拟程序获得I/O操作的具体信息之后,交由硬件模拟代码(Emulation Code)来模拟出本次的I/O操作,完成之后,将本次结果放回到I/O共享页。并通知KVM模块中的I/O操作捕获代码。最后,由KVM模块中的捕获代码读取I/O共享页的操作结果,并把结果返回给客户机中。

在这个操作的过程当中,客户机作为一个QEMU进程在等待I/O时也可能被阻塞。另外,当客户机通过DMA(Direct Memory Access)访问大块I/O时,QEMU模拟程序将不会把操作结果放到I/O共享页,而是通过内存映射的方式将结果直接写到客户机的内存中去,然后通过KVM模块告诉客户机DMA操作已经完成。

优点:

QEMU 可以模拟多种类型的 I/O 设备,包括磁盘、网卡、声卡等,可以满足各种应用场景的需求。用户可以自定义 I/O 设备的参数和配置,以满足特定的需求。

缺点:

每次I/O操作的路径比较长,有较多的VMEntry、VMExit发生,需要多次上下文切换(context witch),也需要多次数据复制,所以性能比较差。

virtio的提出及原理

virtio最初由澳大利亚的一位天才级程序员Rusty Russell编写,是一个在Hypervisor之上的抽象API接口,让客户机知道自己运行在虚拟化环境当中,进而根据virtio标准与Hypervisor协作,从而在客户机当中达到更好的性能(特别是I/O性能)。

virtio的作用

virtio

为各种各样设备(如:网络设备、块设备等等)的虚拟提供了通用的标准化前端接口,增加了各个虚拟化平台的代码复用。而不是各自为政般的使用繁杂的设备虚拟机制。

virtio架构

virtio的基本结构如下

聊聊KVM的半虚拟化virtio驱动

五种前端驱动:

  • 块设备驱动(virtio-blk)
  • 网络设备(virtio-net)
  • PCI仿真(virtio-pci)
  • balloon驱动(virtio-balloon,用于动态管理客户机内存使用)
  • scsi驱动
  • 终端驱动
每一个前端驱动,在hypervisor当中都有一个相匹配的后端驱动

在前后端驱动之间,还定义了两层来支持客户机与QEMU之间的通信。其中virtio这一层是虚拟机队列的接口,虚拟队列(Virtual Queue)接口将前端驱动和后端驱动结合在一起。驱动可以有0个或多个队列。

例如virtio-net网络驱动程序使用两个虚拟队列(一个用于接收,另一个用于发送),而

virtio

块设备驱动只需要一个

virtio-ring: 虚拟队列,通常使用环形缓冲,在客户机与虚拟机管理器之间传输

驱动查看验证

Linux内核现在已经默认开启了virtio的驱动,以我本地的Linux为例:

[root@localhost ~]# uname -r
5.4.243-1.el7.elrepo.x86_64
[root@localhost ~]# find /lib/modules/5.4.243-1.el7.elrepo.x86_64/ -name virtio*.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/drivers/block/virtio_blk.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/drivers/char/hw_random/virtio-rng.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/drivers/char/virtio_console.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/drivers/crypto/virtio/virtio_crypto.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/drivers/gpu/drm/virtio/virtio-gpu.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/drivers/net/virtio_net.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/drivers/nvdimm/virtio_pmem.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/drivers/scsi/virtio_scsi.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/drivers/virtio/virtio.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/drivers/virtio/virtio_balloon.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/drivers/virtio/virtio_input.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/drivers/virtio/virtio_pci.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/drivers/virtio/virtio_ring.ko
/lib/modules/5.4.243-1.el7.elrepo.x86_64/kernel/fs/fuse/virtiofs.ko
           

继续阅读