作者: 孙雷 2021-04-30 09:46:08
云计算
虚拟化 系统虚拟化技术是云计算最重要的核心技术之一。云计算平台的资源池化,资源统一管理以及后续的动态分配都是基于系统虚拟化技术才得以实现的。在计算机系统中,主要有计算资源,存储资源和网络资源。
为企业提供成都网站设计、网站制作、网站优化、全网营销推广、竞价托管、品牌运营等营销获客服务。成都创新互联公司拥有网络营销运营团队,以丰富的互联网营销经验助力企业精准获客,真正落地解决中小企业营销获客难题,做到“让获客更简单”。自创立至今,成功用技术实力解决了企业“网站建设、网络品牌塑造、网络营销”三大难题,同时降低了营销成本,提高了有效客户转化率,获得了众多企业客户的高度认可!
1. virtio的提出
系统虚拟化技术是云计算最重要的核心技术之一。云计算平台的资源池化,资源统一管理以及后续的动态分配都是基于系统虚拟化技术才得以实现的。在计算机系统中,主要有计算资源,存储资源和网络资源。所以,系统虚拟化技术又可以细分为计算虚拟化,存储虚拟化和网络虚拟化。其中存储设备和网络设备一般都是以外设的形式和CPU通过I/O总线连接起来的,所以存储虚拟化和网络虚拟化又可以统一归类为I/O虚拟化技术。我们这里介绍的virtio就是当前最流行的I/O虚拟化技术。
I/O虚拟化技术可以分成两种,一种是全虚拟化,另外一种是半虚拟化。
2. virtio总体设计思想
virtio的总体设计思想可以概括为以下四点:直观,高效,标准化,可扩展。
1) 直观:virtio设备被设计的和之前的硬件设备一样,支持常用的总线机制,比如中断和DMA。这样的设计使得驱动开发人员能够快速上手,完成virtio设备驱动的开发。
2) 高效:virtio设备能支持单独的输入和输出通道,在大量数据传送的时候也能保持高效。
3) 标准化:对硬件架构和操作系统环境没有依赖,支持多种不同总线的传输机制(在virtio规范1.1中支持PCI, MMIO和Channel I/O总线。比如在那些不支持PCI的嵌入式设备还可以通过使用MMIO的方式使用virtio)。
4) 可扩展:在virtio设备发现和设备初始化的时候通过引入特性位(Feature Bits)标识符实现动态适配的协商机制,能够保证virtio前端驱动和后端模拟设备之间的兼容性。
3. virtio基本原理
接下来,我们以目前使用最广泛的QEMU/KVM场景为例子进一步解释virtio的基本原理。虚拟机在物理主机上是一个QEMU的进程,运行在用户态。虚拟机内部的virtio前端驱动所申请的缓存被映射到设备空间中,也在QEMU的地址空间里,这样QEMU就可以通过共享内存的方式对这些缓存进行读写操作。通过这样的方式,实现了virtio前端驱动程序(虚拟机Linux内核的驱动)和后端模拟设备(QEMU后端设备模拟程序)之间数据传输的零复制,进而大幅度提高了虚拟机的I/O性能。
virtio在虚QEMU拟机内核中实现了前端驱动,在QEMU中实现了后端模拟设备,前后端之间通过虚拟队列(Virtqueue)通信交换数据。针对不同的总线机制,virtio设备有不同的实现方式,因为PCI设备是最广泛使用的设备,所以我们以virtio的PCI网卡为例子进行讲解。virtio-net前后端的实现如图2所表示。
图[1]. virtio-net前后端在QEMU/KVM中的实现
在虚拟机启动之后,virtio前端驱动会把自己标识成一个PCI设备,其中包括PCI厂家标识符,PCI设备标识符。这样虚拟机的内核可以基于这个标识符判断使用哪种驱动程序。因为虚拟机中的Linux内核已经包括了virtio驱动程序,所以virtio驱动会被调用去初始化这个virtio设备。除了完成PCI设备通常的初始化操作之外,virtio前端驱动还在初始化的过程中和后端设备模拟程序协商特性位(Feature Bits),并把最终的结果记录在设备状态(Device Status)中。具体的实现代码可以参考内核代码在linux-3.10.0-957.1.3.el7/drivers/virtio/virtio.c中的virtio_dev_probe()函数,如图2所示。
图2. virtio设备初始化,协商特性并最终设置设备状态位
这里有两个比较重要的数据结构需要介绍一下。
虚拟队列(Virtqueue)是被用来在virtio前端驱动和virtio后端模拟设备之间双向数据传输的数据结构。每个virtio设备都维护着一个或者多个虚拟队列。以virtio网络设备为例,它至少维护两个虚拟队列,一个用来存储要发送的数据,一个用来存储接收的收据。每个虚拟队列数据结构都由三部分组成,分别是descriptor table,available ring和used ring。
图3. Virtio规范中虚拟队列的定义
图4. used ring和available ring在virtio规范中的定义
下面我们以虚拟机发送数据为例,结合Linux 3.10和QEMU1.5的代码实现,详细说明一下在QEMU/KVM场景下具体的实现过程。
virtio前端驱动填充数据包,并发出通知
QEMU虚拟机内的virtio网卡驱动在初始化的时候,会和其他的网络驱动一样注册发送函数xmit_skb()。具体的实现如图5,6所示,所以虚拟机内的virtio网卡发送数据的时候,会调用预先注册的函数xmit_skb()。要发送的数据会调用virtqueue_add_outbuf()放置在available ring中。最终在virtqueue_add_outbuf()函数中,会调用virtqueue_kick()函数,并进一步调用virtqueue_notify()函数。在virtqueue_notify()函数中,如图7所表示的virtio前端通过I/O写寄存器的方式通知virtio后端模拟设备。这部分前端驱动的代码在drivers/virtio/virtio_ring.c中。
图5. virtio设备发送数据报文
图6. virtio前端驱动通知QEMU
图7. virtio通知函数最终会写寄存器
虚拟机virtio前端驱动程序发送通知的函数最终是执行I/O写指令。在QEMU/KVM环境中,虚拟机执行I/O指令,会触发VMExit。在KVM的VMExit代码中会判断退出的原因,I/O操作对应的处理函数是handle_io(),具体的代码在linux-3.10.0-957.1.3.el7/arch/x86/kvm/vmx.c,如图8所示。最终再经由KVM通知到QEMU中的virtio-net后端模拟设备,其中还涉及到KVM和eventfd等通信机制,因限于篇幅在这里不详细描述了。
图8. KVM中处理I/O操作导致的VMExit代码
如图8所表示的,在接收到来自KVM的通知之后,QEMU后端设备模拟程序会调用virtio_queue_host_notifier_read()函数,进而调用预先注册的函数virtio_ioprt_write()处理来自前端驱动的I/O写操作。在接收到前端发来的通知之后,会调用virtio_queue_notify()函数进行处理。在接收网络数据包的时候,virtio_queue_notify()会再进一步调用virtio-net网络设备注册的数据包接收函数virtio_net_handle_rx()。如图9所表示的,在qemu_flush_queued_packets()中,QEMU会把数据复制到对应的队列中(QEMU中对应后端的不同tap都维护着不同的队列),之后再调用qemu_notify_event()通知virtio前端,最终会调用kvm_set_irq()触发vCPU的中断的方式通知virtio前端。
图9. virtio后端设备接收通知后的处理
图10. virtio-net预先注册的数据报接收函数
图11. virtio后端设备处理前端发送的数据包
参考链接:
【1】Deep dive into Virtio-networking and vhost-net(https://www.redhat.com/en/blog/deep-dive-virtio-networking-and-vhost-net)
本文转载自微信公众号「Linux阅码场」,可以通过以下二维码关注。转载本文请联系Linux阅码场公众号。
孙雷,曾在联想研究院和NEC研究院工作10年,任资深研究员,研发经理。
负责云计算底层技术,包括软件定义网络(SDN),云网络(OpenStack),
数据面加速等等。
2020年起开始创业,北京守志科技有限公司创始人。
分享题目:虚拟化之Virtio-Net基础篇
URL链接:http://www.shufengxianlan.com/qtweb/news24/546224.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联