虚拟化

 

  在系统中加入虚拟化层,对资源进行空间上的模拟、时间上的分割,虚拟化成多份资源,供上层使用。

1、cpu虚拟化

  全虚拟化:在hypervisor中,对客户机的所有指令进行翻译,然后交给操作系统os执行。客户机机操作系统运行在ring1级别,宿主机操作系统运行在ring0级别。

虚拟化_虚拟化

  半虚拟化:只有特权指令、敏感指令在hypervisor进行翻译,调用宿主机操作系统指令;其他指令在hypervisor层调用hypercall返回对应在宿主机的结果。但是此操作需要改客户机OS代码,不通用,适配性不好。

虚拟化_虚拟化_02 

  为解决此问题,inter提出VT-x 技术。引入根模式、非根模式;客户机运行在根模式,其对应ring0级别;宿主机运行在ring0以下级别

虚拟化_虚拟化_03

  非根模式下敏感指令被重新定义,能不经过虚拟化就直接运行或通过在陷入模拟的方式来处理;根模式下处理方式没有变化。

  非根模式下敏感指令引起的陷入称为:VM-EXIT;根模式切换为非根模式下称为:VM-ENTRY,此操作由宿主机发起调度某个客户机运行。

  VMCS保存虚拟cpu相关的状态,如在根模式、非根模式下寄存器的值等。

2、内存虚拟化

  对地址空间虚拟化,引入了客户机物理地址空间,需两次内存地址进行转换。GVA->VPA->HPA。客户机虚拟地址到客户机物理地址的转换是客户机操作系统通过客户机状态与CR3指向的页表决定;客户机物理地址到宿主机物理地址的转换是通过宿主机来决定,在物理内存分配给客户机时就决定的。

3、I/O半虚拟化

虚拟化_虚拟化_04

 

 

  宿主机使用虚拟交换机连接虚拟机,虚拟交换机中安装dpdk vhost后端,虚拟机中有dpdk virtio前端程序。前后端通过virtio的虚拟队列通信,由虚拟机发送的报文到虚拟交换机,由转发逻辑

到物理网卡中。

  virtio在pci层上定义了virtqueue,用于连接前后端程序。可以配置一个或多个队列;virtio使用两个队列:发送队列和接受队列。

  传统网卡队列:前端指针(virtio驱动使用)和后端指针(设备代表virtio后端驱动,如vhost)初始化都为0;当前段驱动要发送n个数据包时,填充到前端指针描述符中,网卡能看到前端指针的变化来更新后端指针的描述符,后端指针更新完毕后,网卡数据处理完成,等待下次任务到来。这种模式消费者依赖于生产者完成,生产者没有完成时消费者无法跟新。

  双网卡队列中描述符只能顺序执行,前一个描述符没有处理完,后一个描述符只能等待。virtio虚拟队列不存在这个限制,图如下:

虚拟化_虚拟化_05

  虚拟队列初始化和设备初始化紧邻,使用设备寄存器地址空间。

  1. 选择虚拟机队列写入队列选择寄存器
  2. 读取队列容量寄存器,获取队列大小,0表示设备不可用
  3. 分配队列需要的内存,把物理地址写入寄存器
  4. 用一个向量处理队列中断,把向量号写入中断寄存器

  描述符列表:客户端一侧的数据缓冲区,共客户机和宿主机之间数据传递。当客户机发送的数据过大描述符列表装不下时,通过描述符链串起来。

  可用环表:由驱动写入、设备读出;设备取出环表中的描述符后,当缓冲区为可读时表示客户机有数据发出(驱动发送数据到设备);当缓冲区可写时表示有数据接受。

  已用环表:设备写入,驱动读出;设备使用完可用环表描述符后放入用环表,通知驱动回收描述符

  驱动通过描述符列表和可用表环列表给设备使用,设备使用完后通过已用表环通知驱动回收描述符。

  单数据帧发送优化:前段驱动和后端驱动运行在不同的core上,前段驱动更新和后端驱动读取可用表环会触发不同core之间可用环表的cache迁移,为解决这一问题,dpdk固定可用环表和描述符表项的映射。可用环表的跟新只需要更新环表自身指针,固定的环表可避免不同core之间的cache迁移。

  virtio-net三要素:虚拟队列机制(用于前后端数据交互)、消息通知机制(客户机到宿主机消息通知)、中断机制(宿主机到客户机中断请求--收包)

  虚拟机利用qume、kvm通信

虚拟化_虚拟化_06

 

 

  qume利用kvm模拟整个系统运行环境,包括处理器、外设等;tap是内核中的虚拟以太网设备。

  客户机发包时,通过消息通知机制通知kvm,并退出到qume,有其对tap设备读写。宿主机、客户机、qume来回切换,cpu指令级切换带来性能开销,主要集中在消息通知机制和数据通道

  1. 数据从tap拷贝到qume和从qume拷贝到客户机;
  2. 报文到达tap设备时内核发出并送到qume,qume利用ioctl想kvm请求中断,kvm发送中断到客户机。

  内核态vhost

  qume不再负责virtio-net的虚拟队列工作,由vhost在内核态来负责及报文送达消息通知的中断

虚拟化_虚拟化_07

  1. 从tap收到报文,vhost-net把报文送到虚拟队列的数据区,客户机接受报文
  2. 报文从tap到达vhost-net时,通过kvm向客户机发送中断,通知其接受报文

  如上操作前提时qume要共享如下信息:

  1. qume共享客户机内存布局,host-net可以得到客户机的物理地址到宿主机的物理地址转换
  2. qume共享虚拟队列地址,host-net要对虚拟队列直接进行读写操作
  3. qume共享kvm向客户机上virtio-net发送中断的描述符,host-net收到报文后通知客户机取走报文
  4. qume共享kvm向客户机virtio-net pci设备空间的读写描述符,当客户机有数据要发送时,pci设备上写描述符被触发,host-net可收包

  vhost-user 

  卸载vhost-net在内核中处理报文,迁移至用户态。vhost-user为每个vm创建一个端口,实现virtio后端逻辑:虚拟机的收发报请求、数据拷贝等;每个vm运行于qume进程内,qume整合了kvm的通信逻辑,成为vm主进程,包含vcpu等线程。vhost-user和qume都是用户态进程,在数据拷贝时候必须由内存共享。

虚拟化_虚拟化_08

 

 

  发包:vm要发送报文时,qume发送ioeventfd给kvm,使其通知vhost-user拷贝数据。当由数据包到vhost-user后(dpdk旁路内核直接收包到应用进程dpdk空间:vfio、igb_uio),通过irqfd通知kvm,使其通知guest收报文。