接前一篇文章:
本文内容参考:
《 QEMU /KVM》源码解析与应用 —— 李强,机械工业出版社
特此致谢!
上一回对于virtio进行了简介,并说明了其基本原理以及框架。对于virtio框架,再来回顾一下。
virtio框架如下图所示:
virtio是一种利用半虚拟化技术提供I/O性能的框架,其是一种 前后端架构 ,包括 前端驱动(Front-End Driver) 和 后端设备(Back-End Device) 以及 自身定义的传输协议 。
- 前端驱动
前端驱动为 虚拟机 内部的virtio模拟设备对应的驱动,每一种前端设备都需要有对应的驱动才能正常运行。前端驱动的主要作用是:1)接收用户态的请求;2)然后按照传输协议将这些请求进行封装;3)再写I/O端口;4)发送一个通知到 QEMU 的后端设备。
- 后端设备
后端设备则是在QEMU中,用来接收前端驱动发过来的I/O请求,然后从接收的数据中按照传输协议的格式进行解析。对于网卡等需要实际物理设备交互的请求,后端驱动会对物理设备进行操作,从而完成请求,并且会通过中断机制通知前端驱动。
- virtio队列
virtio前端和后端驱动的数据传输通过virtio队列(virtio queue,virtqueue)完成,一个设备会注册若干个virtio队列,每个队列负责处理不同的数据传输。这些队列有的是控制层面的队列、有的是数据层面的队列。virtqueue是通过vring实现的。vring是虚拟机和QEMU之间共享的一段环形缓冲区。当虚拟机需要发送请求到QEMU的时候就准备好数据,将数据描述放到vring中,写一个I/O端口。然后QEMU就能够从vring中读取数据信息,进而从内存中读出数据。QEMU完成请求之后,也将数据结构存放在vring中,前端驱动也就可以从vring中得到数据。
此处也有另外一种观点和视角,将virtio架构分为四层:
- 前端驱动
首先,在虚拟机里面的virtio前端,针对不同类型的设备有不同的驱动程序,但是接口都是统一的。例如,硬盘就是virtio_blk,网络就是virtio_net。
要点:
1) 运行在虚拟机中 ;
2) 针对不同类型的设备有不同的驱动程序,但是与后端驱动交互的接口都是统一的 ;
3) 在Qemu+KVM虚拟化环境中,源码位于KVM源码(Guest 内核)中 。
- 后端设备(后端驱动)
其次,在宿主机的QEMU里边,实现virtio后端的逻辑,主要就是操作硬件的设备。例如,通过写一个 物理机 硬盘上的文件来完成虚拟机写入硬盘的操作。再如,向内核协议栈发送一个网络包完成虚拟机对于网络的操作。
要点:
1) 运行在宿主机中 ;
2) 实现virtio后端的逻辑,主要是操作硬件设备 。比如向内核协议栈发送一个网络包完成虚拟机对于网络的操作。
3) 在Qemu+KVM虚拟化环境中,源码位于Qemu源码中 。
- virtio层与virtio-ring层
在virtio的前端和后端之间,有一个通信层,里边包括virtio层和virtio-ring层。virtio这一层实现的是虚拟队列接口,算作前后端通信的桥梁;而virtio-ring则是该桥梁的具体实现。
- virtio层
要点:
1) virtio层实现虚拟队列接口,作为前后端通信的桥梁 ;
2) 不同类型的设备使用的虚拟队列数量不同 ;
3) Linux 内核源码/ 源码位于drivers/virtio/virtio.c中 。
- virtio-ring层
要点:
1) virtio-ring层是虚拟队列的具体实现 ;
2) 源码位于Linux内核源码/driver/virtio/virtio_ring.c中 。
virtio使用virtqueue进行前端与后端的高速通信。 不同类型的设备队列数目不同 。virtio-net使用两个队列,一个用于接收、另一个用于发送;而virtio-blk仅使用一个队列。
如果客户机要向宿主机发送数据,则它会 将数据的buffer添加到virtqueue中 ,然后 通过写入寄存器通知宿主机 。这样宿主机就可以从virtqueue中收到buffer里边的数据了(与前述机制一个意思: 当虚拟机需要发送请求到QEMU的时候就准备好数据,将数据描述放到vring中,写一个I/O端口。然后QEMU就能够从vring中读取数据信息,进而从内存中读出数据 。QEMU完成请求之后,也将数据结构存放在vring中,前端驱动也就可以从vring中得到数据)。
欲知后事如何,且看下回分解。