1、前言

简言之,virtio是对于半虚拟化管理程序(para-virtualizedhypervisor)中设备的一个具象层。virtio是RustyRussell为了支持他自己的虚拟化方案lguest而开发的。

这篇文章先对半虚拟化和设备模拟技术进行介绍,之后找寻virtio技术实现中的一些细节。

本文基于kernel2.6.30版本的virtio框架进行讲解。

Linux是hypervisor的“试验场”。Linux提供了许多具有不同特征和优势的虚拟化解决方案。诸如KVM(Kernel-basedVirtualMachine),lguest、和用户态的Linux。由于这种虚拟化解决方案各自都有不同的需求,因而在Linux实现这种虚拟化解决方案给操作引起了不小的重任,其中一个负担就是设备虚拟。

virtio没有为不同类型的设备(如:网路设备、块设备等)提供不同的设备模拟机制,而是通过标准化的插口为这种设备模拟提供了一个通用的后端,因而降低了代码在跨平台时的重用性。

2、全虚拟化vs半虚拟化

我们先来快速讨论两种完全不同的虚拟化方案:全虚拟化和半虚拟化。

在全虚拟化中,顾客机操作系统运行在hypervisor之上,而hypervisor运行在裸机之上。顾客机不晓得它是在虚拟机还是化学机中运行,在全虚拟化中顾客机不须要更改操作系统就可以直接运行。

与此相反的是,在半虚拟化中,顾客机操作系统除了须要感知其运行于hypervisor之上,还必须包含与hypervisor进行交互就能带来更高效率的代码。(如图1所示)。

在全虚拟化中,hypervisor必须进行设备硬件的模拟,也就是须要模拟最底层的通讯机制(如:网卡驱动)。虽然这些具象模拟机制很干净,但这样做同时也是最低效率和最复杂的。

在半虚拟化中,顾客机与hypervisor可以协作工作以增强模拟效率。半虚拟化的缺点就是顾客机操作系统必须感知到它运行于虚拟机之中,并且须要对顾客机操作系统进行更改能够工作。

图1:全虚拟化与半虚拟化环境中的设备模拟

硬件辅助虚拟化也在不断发展。新处理器降低中级指令,使顾客机操作系统和hypervisor之间的的转换更有效率。硬件I/O虚拟化同样也在不断发展(比如SR-IOV、MR-IOV)。

(如图1右边所示)在传统的全虚拟化环境中,hypervisor必须捕获I/O恳求,之后模拟出真实硬件的行为。虽然这样提供了很大的灵活性(可以运行不用进行更改的操作系统)linux 网卡虚拟化,但这样却会导致效率低下。

(如图1右边所示)在半虚拟化环境中。顾客机操作系统晓得其运行在虚拟机之中,但是加入了作为后端的驱动程序。hypervisor为特定设备模拟实现前端驱动程序。这儿的前后端驱动就是virtio的构架的组成部份,为模拟设备的访问的开发提供标准化插口,以增强代码重用率及降低转换效率。

Virtio代替品:

virtio并不是半虚拟化领域的惟一方式,Xen也提供了类似的半虚拟化设备驱动linux系统装win7,VMware也提供了名为GuestTools的半虚拟化构架。

3、Linux顾客机的一种具象

如前节所述,virtio是半虚拟化hypervisor中一组通用模拟设备的具象。

这些设计容许hypervisor输出一组通用的模拟设备,并通过通用的应用程序编程插口(API)使它们可用。

如图2所示,使用半虚拟化hypervisor,顾客机实现了一组通用的插口linux,并在一组前端驱动旁边进行特定设备模拟。前端驱动并不须要做到通用,只要它能实现后端所需的各类行为即可。

图2:使用virtio进行驱动程序具象

在实际实现中,使用用户空间的QEMU程序来进行设备模拟,所以前端驱动通过与用户空间的hypervisor进行通讯,便于通过QEMU进行I/O操作。QEMU是系统模拟器,不仅提供顾客机操作系统虚拟化平台外,还提供整个系统的设备模拟(PCIhost控制器、磁盘、网络、视频硬件、USB控制器和其他硬件设备)。

virtioAPI依赖于一个简单的缓冲区具象来封装顾客机的和数据恳求。让我们瞧瞧virtioAPI及其组件的内部结构。

4、virtio构架

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

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

图3:virtio构架

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

5、概念层级

从顾客机的角度来看,对象层次结构的定义如图4所示。

最顶楼是virtio_driver,它代表顾客机中的后端驱动。与此驱动程序所匹配的设备通过virtio_device结构体进行封装(Guest设备的表示方式)。virtio_config_ops结构体定义了对virtio设备进行配置的操作。virtio_device被virtqueue所引用(包括被virtqueue所使用的virtio_device的引用)。最后,每位virtqueue对象都引用virtqueue_ops对象,该对象定义了用于处理hypervisor驱动程序的底层队列操作。

尽管队列操作是virtioAPI的核心,但我们只会对其进行简略讨论,之后更详尽地阐述virtqueue_ops操作。

图4:virtio后端对象层次结构

该过程从创建virtio_driver开始,此后通过register_virtio_driver进行注册。

virtio_driver结构定义了下层设备驱动,驱动程序支持的设备ID列表、功能列表(取决于设备类型)和反弹函数列表。当hypervisor辨识出与设备列表中的设备ID所匹配的新设备时,会调用probe函数(在virtio_driver对象中定义)来传递virtio_device对象。此对象与设备的管理数据一起被缓存(以依赖于驱动程序的形式)。

按照驱动程序类型,可以调用virtio_config_ops函数来获取或设置特定于设备的选项(比如,获取virtio_blk设备的c盘的读/写状态或设置块设备的块大小)。

请注意,virtio_device不包含对virtqueue的引用(但virtqueue引用了virtio_device)。您可以使用带有find_vq函数的virtio_config_ops对象来辨识与此virtio_device关联的virtqueue。此对象返回与此virtio_device实例关联的虚拟队列。find_vq函数还容许为virtqueue指定反弹函数(参见图4中的virtqueue结构),用于通过hypervisor的响应缓冲区来通知顾客机。

virtqueue是一个简单的结构,它包含一个可选的反弹函数(当hypervisor消耗缓冲区时调用它),对virtio_device的引用,对virtqueue操作的引用以及对特殊priv的引用(指的是使用的底层实现)。虽然反弹是可选的,但可以动态启用或禁用反弹函数。

然而这个层次结构的核心是virtqueue_ops,它定义了命令和数据怎么在Guest和hypervisor之间联通的。让我们首先从阐述在virtqueue中添加或删掉的对象开始。

6、virtiobuffer

Guest(后端)驱动程序通过缓冲区与hypervisor(前端)驱动程序进行通讯。对于I/O操作来说,Guest提供一个或多个表示恳求的缓冲区。

比如,您可以提供三个缓冲区,第一个缓冲区表示读取恳求,此后的两个缓冲区表示响应数据。在内部,此配置表示为一个分散-搜集列表(列表中的每位条目被表示为一个地址和一个宽度)。

7、核心API

通过virtio_device联接Guest驱动程序和hypervisor驱动程序,最常见的是通过virtqueues。virtqueue支持它自己的由五个函数(见图4)组成的API。

使用第一个函数add_buf向hypervisor发起恳求。该恳求采用上面讨论的分散-集聚列表的方式表示。为了实现add_buf操作,guest将进行将request在virtqueue中进行入队、分散搜集列表(地址和宽度的链表)、配置输出条目的缓冲区数目(用于底层的hypervisor)以及配置输入条目的数目(hypervisor将为其储存数据并返回给Guest)的一系列操作。

当通过add_buf在将向hypervisor的恳求消息入队时,guest可以使用kick函数将新恳求通知给hypervisor。为了获得最佳性能,Guest应在通过kick通知之前将尽可能多的缓冲区放置到virtqueue上。

来自hypervisor响应消息可以通过get_buf获得。Guest可以简单地通过调用此函数进行鉴权或通过virtqueue提供的callback函数异步的形式等待响应消息通知。当Guest得悉缓冲区可用时,调用get_buf返回已完成的缓冲区。

virtqueueAPI中的最后两个函数是enable_cb和disable_cb。您可以使用这种函数来启用和禁用callback处理(在virtqueue中通过find_vq函数初始化的callback函数)。请注意,callback函数和hypervisor坐落不同的地址空间中,因而是通过间接对hypervisor的调用进行的(比如kvm_hypercall)。

缓冲区的格式、顺序和内容仅对后端和前端驱动程序有意义。内部传输(当前是通过ring实现的)仅联通缓冲区而且不晓得缓冲区的内部表示方式。

8、virtio驱动示例

您可以在Linux内核的./drivers子目录下找到各类后端驱动代码。virtio网路驱动在可以在./driver/net/virtio_net.c中找到,virtio块驱动可以在./driver/block/virtio_blk.c中找到。

./driver/virtio子目录下提供了virtio插口实现(virtio设备、驱动程序、virtqueue和ring)。virtio也被用在了高性能估算(HPC)研究之中,使用共享显存在虚拟机之间进行通讯。具体来说linux 网卡虚拟化命令,这是通过使用virtioPCI驱动程序的虚拟PCI插口实现的。

您如今可以在Linux内核中使用这些半虚拟化基础设施。你所须要的就是一个用作hypervisor的内核,guest内核和拿来进行设备模拟的QEMU。你可以使用KVM(一个存在于宿主机内核中的模块)或则RustyRussell的lguest(一个更改过的Linux内核)。两种方案都支持virtio(以及用于进行系统模拟的QEMU和用于虚拟化管理的libvirt)。

Rusty的工作成果是一个更简单的半虚拟化驱动程序代码库和更快的虚拟设备模拟,但更重要的是,人们发觉virtio可以提供比当前商业解决方案更好的性能(网路I/O的2-3倍)。这些性能提高是有代价的,但若果使用Linux作为您的hypervisor和guest,那将是十分值得的。

9、关于未来

虽然您可能永远不会为virtio开发后端或前端驱动,但virtio整个构架,值得更详尽地了解。

virtio提升了半虚拟化I/O环境中的效率,virtio是基于之前在Xen中的工作进行重构的。Linux继续证明自己是新虚拟化技术的研究平台。virtio是将Linux作为hypervisor比使用其他类型的hypervisor具有竞争力的另一个反例。

(正文完)

end

Reference:

本文原创地址://gulass.cn/ldxnhjjfakjj.html编辑:刘遄,审核员:暂无