KVM是必须使用硬件虚拟化辅助技术(如Intel VT-x、AMD-V)的Hypervisor,在cpu运行效率方面有硬件支持、其效率是比较高的;

在有Intel EPT特性支持的平台上,内存虚拟化的效率也较高。

不过,KVM在I/O虚拟化方面,传统的方式是使用QEMU纯软件的方式来模拟I/O设备,其效率很差。(性能和兼容性的博弈)

QEMU模拟过程

  在使用QEMU模块I/O的情况下,当客户机中的设备驱动程序(Device Driver)发起I/O操作请求之时,KVM模块(Module)中的I/O操作捕获代码会拦截I/O请求,然后在经过处理后将本次I/O请求。然后经过处理后将本次I/O请求的信息存放到I/O共享页(sharing page),并通知用户空间的QEMU程序。Qemu模拟程序获取I/O操作的具体信息之后,交由硬件模拟代码(EmulationCode)来模拟出本次的I/O操作,完成之后,将结果放回到I/O 共享页,并通知KVM模块中的I/O操作捕获代码。最后,由KVM模块中的捕获代码读取I/O共享页中的操作结果,并把结果返回到客户机中。当然,在这个操作过程中客户机作为一个Qemu进程在等待I/O是也可能被阻塞。另外,当客户机通过DMA(Direct Memory Access)访问大块I/O之时,QEMU模拟程序将不会把操作结果放到I/O共享页中,而是通过内存映射的方式将结果直接写到客户机内存中区,然后通过KVM模块告诉客户机DMA操作已经完成。

virtio I/O半虚拟化磁盘

前端驱动(frondend,如virtio-blk、virtio-net)是在客户机中存在的驱动程序模块,而后端处理程序(backend)是在QEMU中实现的。

在前后端定义了两层来支持客户机与QEMU之间的通信。

virtio层,在概念上,将前端驱动程序附加到后端程序。一个前端驱动程序可以使用0个或多个队列,具体数量取决于需求。(virtio-net 2个队列,virtio-blk一个队列)

虚拟队列实际上被实现为跨越客户机操作系统和Hypervisor的衔接点,但该衔接点可以通过任意方式实现。

virtio-ring实现了环形缓冲区(ring buffer),用于保存前端驱动和后端处理程序执行的信息,并且该环形缓冲区可以一次性保存前端驱动的多次请求,并且交由后端驱动去批量处理,最后实际调用宿主机中设备驱动实现物理上的I/O操作,这样做就可以根据约定实现批量处理而不是客户机中每次I/O请求都需要处理一次,从而提高客户机与Hypervisor信息交换的效率。

virtio、virtio_ring、virtio_pci等驱动程序提供了对virtio API的基本支持,是使用任何virtio前端驱动都必须使用的,而且他们的加载还有一定的顺序,应该按照virtio、virtio_ring、virtio_pci的顺序加载,而virtio_net、virtio_blk这样的驱动可以根据实际需要进行选择性的编译和加载。

 

virtio ballooning

气球中的内存时可以供宿主机使用的(但不能被客户机访问或使用),所以,当客户机内存紧张,空余内存不多的时候,可以请求客户机回收利用已分配给客户机的部分内存,客户机就会释放其空下内存,此时若客户机空闲内存不足,可能还会回收部分使用中的内存,可能会将部分内存换出到客户机的交换分区(swap)中,从而是内存气球充气膨胀,进而是宿主机回收气球中的内存用于其他进程。反之,当客户机内存不足时,也可以让客户机的内存气球压缩,释放出内存气球中的部分内存,让客户机使用更多的内存。(真绕!)

KVM中ballooning的工作过程主要有如下步骤:

  1)  Hypervisor(即KVM)发送请求到客户机操作系统让其归还一定数量的内存给Hypervisor

  2)  客户机操作系统中的virtio_balloon驱动接收到Hypervisor的请求。

  3)  virtio_balloon驱动使客户机的内存气球膨胀,气球中的内存就不能被客户机访问。如果此时客户机中的内存剩余不多,并且不能让内存气球膨胀到足够大以满足Hypervisor的请求,那么virtio_balloon驱动也会尽可能地提供内存使气球膨胀,尽量去满足Hypervisor的请求中的内存数量(即使不一定能完全满足)。

  4)  客户机操作系统归还气球中的内存给Hypervisor

  5)  Hypervisor可以将气球中得来的内存分配到任何需要的地方

  6)  即使从气球中得到的内存没有处于使用中,Hypervisor也可以将内存返还到客户机中,这个过程为:Hypervisor发请求到客户机的virtio_balloon驱动;这个请求是客户机操作系统压缩内存气球;在气球中的内存被释放出来,重新由客户机访问和使用。

 

vhost_net后端驱动

virtio在宿主机中的后端处理程序(backend)一般是由用户空间的QEMU提供的,然而,如果对于网络IO请求的后端处理能够在内核空间开完成,则效率会更高提高网络吞吐量和减少网络延迟。

vhost-net的驱动模块,作为一个内核级别的后端处理驱动,将virtio-net的后端处理任务放到内核空间中执行,从而提高效率。

“使用网桥模式”

-net tap,[,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h][,vhostforce=on|off]

一般来说,使用vhost-net作为后端处理程序可以提高网络的性能。不过,对于一些使用vhost-net作为后端的网络负载类型,可能使其性能不升反降。特别是从宿主机到其客户机之间的UDP流量,如果客户及处理接受数据的速度比宿主机发送的速度要慢,这是就容易出现性能下降。在这种情况下,使用vhost-net将会是UDPsocket的接受缓冲区更快的溢出,从而导致更多的数据包丢失。因此在这种情况下不使用vhost-net,让传输速度稍微慢一点,反而会提高整体的性能。

使用qemu-kvm命令行时,加上“vhost=off”

<interface type="network">
  <model type="virtio"/>
  <driver name="qemu"/>
</interface>

virtio_blk 驱动使用virito API为客户机提供了一个高效访问块设备I/O的方法。

在QEMU/KVM中对块设备使用virito,需要在两方面进行配置:客户机中的前端驱动模块virito_blk和宿主机中的QEMU提供后端处理程序。