基于Xen实现一种domain0和domainU的应用层数据交互高效机制

项目里有一个需求,domain0的应用层需要定时给domainU(hvm windows)的应用层传递一块数据,原来的方案是在domainU的应用层架设一个http服务器,监听在某个端口,然后需要塞数据时,domain0的应用程序连接该端口,并通过http send发送数据。发送完会等待domainU 的应用程序返回一个标记。

无意间看到这篇论文《全虚拟化HVM和半虚拟化PV虚拟平台通信机制分析》,里边介绍hvm情况下domainU与Doamin0用户层的数据交互机制,根据文章介绍,尝试设计一种交互方式: 1. domainU 里增加一个自定义驱动(叫datafront),该驱动对应用层提供接口,当应用层调用特定系统调用或函数时,datafront触发vmexit. 该vmexit对应hypervisor_callback会检测一片共享内存区,如果有特定数据就读取到domainU 应用层。2. domain0里修改应用层的 qemu-dm守护程序,或者自己写一个应用程序,当需要塞数据时,将数据写入共享页,然后发出事件通知。 大概的过程就是这样,具体系统后续再考虑。

 

参考: 这里

 

全虚拟化HVM和半虚拟化PV虚拟平台通信机制分析

一.半虚拟化PV虚拟平台通信机制

由于基本上所有的设备驱动都假设自己可以直接访问硬件,同时对硬件拥有完全控制权。但在这虚拟机系统中是不可能的,一个设备通常要为多个Guest Domain服务。为了确保管理性和安全访问,Xen的设备虚拟化采用了分离式设备驱动模型。

当Guest Domain是一个准虚拟化的虚拟机时,虚拟机的内核是被修改过的,它知道自己不是运行在真实的硬件上。其设备是由Xen虚拟机出来的分离设备模型,要靠Frontend  和Backend协同工作来完成通信。分离式设备驱动模型如图示。

 

从图中可以看出,前端驱动(Frontend Driver)位于Guest Domain中,负责接收Guest Domain的I/O处理请求,传递给后端驱动(Backend Driver),并接受来自后端的处理结果返回给Guest  Domain。后端驱动在Domain0中,负责接收来自前端的I/O处理请求,并把请求交给Domain0中的相关驱动来处理,随后把处理结果返回给Frontend。从而完成Guest Domain的I/O操作。

由于前端和后端位于不同的OS中,它们之间的通信要依赖共享内存环和事件通道来进行。共享环是由前端分配的一块共享内存,在前端和后端间共享。在ring上存在两对生产者消费者指针。通过共享内存环,前端和后端可以把I/O请求放入环中和从环中读取,而I/O请求的处理结果也可以通过环进行传递。事件通道则允许前端和后端发送给对方一个确认信息。

Xen采用了共享描述符环来实现上述工作,其原理如下图所示。

 

Guest Domain把数据传输的请求放到I/O环上并更新请求生产者指针;Xen取出请求并更新消费者指针。同样地,当Xen准备好响应数据后,也把一个描述符放到I/O环上并更新应答生产者指针;客户域取出应答交给应用程序并更新响应消费者指针。

需要注意的是,I/O环上的内容并不是共享数据的内容,而是共享数据所在的缓冲区的描述符,这是因为,对于高速DMA设备,用描述符是不合适的。真正的数据传递是借助Grant  Table完成的。同时,这里不要求按顺序处理请求:

Guest OS给每个请求都建立了唯一的相关标识符,这个标识符会在相关的响应上被复制。描述符环只是限定了能够处理请求的规模,并不规定处理顺序,谁先被处理谁后被处理是在将数据映射到描述符环上的时候决定的,这就允许Xen出于调度和优先级的考虑,重新排定I/O操作的顺序。Xen使用事件通道作为有I/O描述符进入队列的异步通知,不管是request请求还是response都可以在同时放入多个描述符项,达到一定的阀值后才发送事件通知。

下图详细展示了在Xen虚拟机系统上,两个准虚拟化的Guest,  Domain1和Domain2之间是如何进行通信的。当Domain1中的应用程序App1要向Domain2中的应用程序App2发送数据包时,它按照如下的步骤完成:

1.  Domain2首先调用send(),将自己用户空间的数据拷贝到内核空间;

2.  Domain2的前端将发送请求放到共享环上,通过更新生产者共享指针完成;

3.  前端将会通过事件通道向Domain0发出一个事件(更新,该事件触发Back-end去获得包含了App1数据的页;back-end读取请求,获得页的物理地址,并发送到响应的Driver;

4.  Xen将Domain2的请求移出共享描述符环,并移动请求消费者指针,同时调度Domain1;

5.  Domain1中的前端在IO环中用一个指向Grant table中空闲页的指针填上接收请求;

6.  当back-end接收到数据报时,它首先检查该数据报属于谁,然后从IO环中移出接收请求,同时从grant  table中获取空闲页,然后将空闲页和数据页交换,并通过Event channel发出接收事件;

7.  前端通知内核,App2接收数据。

 

 

二、Guest Domain是全虚拟化的虚拟机

当Guest  Domian是全虚拟化的虚拟机时,Xen  为它模拟了与真实机器完全一样的抽象平台,Guest Domain不知道自己运行在Hypervisor上。但是,由于硬件虚拟化技术的引入,例如,在VT-x的CPU上,当虚拟机控制结构VMCS(Virtual Machine Control Structure)结构体中定义的那些事件被触发时,就会发生VMExit,从非根状态切换到根状态,导致陷入到Hypervisor中,Guest Domain被切换出去。

VMCS结构包含以下6个组成部分:客户状态域,主机状态域,虚拟机执行控制域,VMExit控制域,VMEntry控制域以及VMExit信息域。虚拟机监控器主要通过配置虚拟机执行控制域来控制虚拟机在非根环境下的执行行为。通过设置虚拟机执行控制域,VMM可以对不同VM的VMCS 分别设置不同虚拟机退出条件,从而实现对不同VM的不同虚拟化策略。

如下图所示,一个全虚拟化的虚拟机要想另外一个目标机器发送数据时,其流程如下:

1.  Guest  Domain中的内核执行In/Out指令触发VMExit,处理器调用Hypervisor设置的VMExit的处理函数。

2.  Hypervisor将I/O指令的具体信息写入DomainU的DM共享的一页I/O共享页中,并通过事件通道通知Domain0,接着Hypervisor阻塞该虚拟机,并调用调度算法。

3.  Hypervisor恢复Domain0的状态,并把执行控制交给Domain0。

4.  Domain0中首先被执行的是注册的回调函数hypervisor_callback,它再调用evtchn_do_upcall。

5.  evtchn_do_upcall里收集有哪些虚拟机有多少I/O请求。

6.  执行控制从Xen0的内核态返回用户程序态。DM本来通过一条select系统调用等待I/O请求,此时得到调度后的DM一旦等到请求到来就返回。

7.  通过读取I/O共享页,DM识别是对哪类外设的访问,调用对应虚拟外设初始化时的回调函数。

8.  根据不同的请求,虚拟外设的回调函数或者只是把虚拟外设的状态写回I/O共享页中,或者发生一次真正的数据拷贝。最后DM仍通过事件通道机制通知Hypervisor处理完毕。

9.  Hypervisor得到通知后,解除对应的请求I/O的Guest  Domain的阻塞。

未来某一时刻,该Guest Domain就可以再次被调度到,继续运行了。上述整个流程如下图所示:

 

 

posted @ 2014-06-30 21:35  cgj  阅读(1130)  评论(0编辑  收藏  举报