虚拟化基本知识及virtio-net初探

news2024/10/5 13:39:30

 

QEMU/KVM是在Linux中被广泛使用的虚拟化技术之一,而virtio作为一个半虚拟化I/O事实上的标准[1],是QEMU/KVM在I/O虚拟化部分的默认实现。virtio-net是virtio标准中的网卡设备,被广泛应用。本文将会沿着虚拟化,virtio半虚拟化I/O,virtio-net的基本情况这条路线逐渐深入。

下面在第一节介绍虚拟化的基本知识和虚拟化IO的相关方法,在第二小节介绍引入virtio的必要性和其基本知识,在第三小节介绍virtio-net的相关内容

虚拟化基本知识

虚拟机已经悄然渗透到我们的生活中,随处可见。比如,申请云服务器的时候,我们申请到的机器就是一台虚拟机;在编写完服务端代码,部署我们的服务时,经常采用docker,kata等容器技术;再比如,我们可能需要VMware来创建一个隔离的环境来运行某些恶意的服务;亦或是自制了一个操作系统,但是又不想用硬件调试,那我们就可以用qemu来进行测试;甚至在新能源的车机上,都可能通过虚拟化技术运行着多套操作系统。看到这里,大家可能自然产生了一个疑问,这么多虚拟化的方法有什么异同,各自又有什么特点和适用场景呢?本节将会带领大家走进虚拟化,一窥虚拟化的基本知识,本小节内容和图片参考[2]、[3]、[8]。在深入之前,首先要确定什么是虚拟化。虚拟化的定义是指抽象和模拟计算机的硬件和软件的技术。计算机系统大概可以划分为四个层次:硬件、操作系统、库和用户程序,如下图所示:

而本节开始的描述的虚拟化场景中,都是抽象了这个结构中的某个层次,并为上层服务提供接口。

根据虚拟化层次的不同,我们可以将虚拟化技术划分为不同的种类:

  • 直接对硬件资源进行虚拟化,也叫做系统级虚拟化。是通过模拟和抽象硬件资源,来提供虚拟化的ISA接口。我们常用的VMware,Xen,qemu都是属于这个类型的。
  • 操作系统层次的虚拟化,也叫做轻量级虚拟化,进程虚拟化。操作系统本身就是对硬件的一种虚拟化,容器技术进一步通过namespace、Cgroups和overlayfs来提供对资源的隔离和限制,实现对操作系统的虚拟化。docker容器就是属于这个类型的。
  • 应用层程序运行环境的虚拟化。对API调用进行虚拟化,无论是通过语言运行时还是拦截调用进行转换,都可以实现对宿主机设备的中的API虚拟化的目标。JVM,Wine等技术就属于这个类型。

本文主要讨论系统级虚拟化,下面介绍系统虚拟化的基本知识。

系统虚拟化

首先介绍一下系统级虚拟化知识中会出现的相关名词

  • Guest OS:运行在虚拟机里面的操作系统
  • Host OS:Type-2架构下支持虚拟机运行的操作系统
  • VMM/Hypervisor:表示虚拟机资源管理器,是负责为虚拟机提供虚拟的硬件资源的,前者通常用于Type-2类型的虚拟机场景,后者通常用于Type-1类型的虚拟机场景
  • Type-1/Type-2:Type-1/Type-2是两种虚拟化的架构,前者表示虚拟机资源管理器直接和硬件交互,后者表示虚拟机资源管理器通过Host OS和硬件交互。
    Type-1架构如下图所示:

Type-2架构如下图所示:

接着我们看一下在系统虚拟化的过程中有哪些需要实现的内容。系统虚拟化的任务一般要分为CPU虚拟化,内存虚拟化,I/O虚拟化。本文的virtio半虚拟化就是I/O虚拟化方法的一种,目前I/O虚拟化速度成为整个系统的瓶颈,所以越来越被人重视,而virtio半虚拟化就是解决这个问题的利器。CPU虚拟化是将CPU的能力抽象出来,可以给上层模拟出多个相同或者不同架构的CPU。根据指令执行所需要的权限不同,指令可以分为特权指令和非特权指令。同时根据指令执行是否访问物理资源,我们可以将指令分为敏感指令和非敏感指令。所以如果所有的敏感指令都是特权指令,我们就很容易实现CPU虚拟化,我们将这样的指令集称为可虚拟化架构,反之称之为不可虚拟化架构。内存虚拟化需要保证虚拟机看到的是从0开始并且连续的,这是通过引入了一层新的地址空间GPA来解决的,Host OS直接接触物理内存,所以会有HPA(Host Phyical Address),同时Host OS上面运行着Guest OS作为自己的程序,所以此时引入了GPA(Guest Phyical Address)和GVA(Guest Virtual Address)。当Guest OS想要访问内存时,首先从GVA通过页表找到GPA,再通过页表找到HPA,如下图所示:

最后来到了I/O虚拟化部分。I/O虚拟化需要复用外设的接口资源,提供给客户机使用。外设的接口资源通常由MMIO寄存器、PMIO寄存器、中断模块和DMA模块四部分组成。对I/O的的虚拟化需要同时考虑这四种资源。I/O虚拟化有三种方式,分别为模拟、透传、半虚拟化:

  • 模拟是通过对指令的模仿进行虚拟化,模拟不需要专用硬件的支持,但是性能比较弱。
  • 透传是Guest OS直接访问I/O硬件,不经过Host OS,这样的虚拟化有更高的性能;但是透传在设备共享,动态迁移场景下有不小的问题。
  • 半虚拟化的事实标准是Virtio。是通过一个Host OS和Guest OS共同认可的通信方式,加快通信的速度,在具有适用性的同时,大幅提升了速度。这也是本文接下来的主角,会在virtio基本知识小节介绍virtio的具体情况。

在介绍virtio的具体知识之前,再简单介绍一下QEMU/KVM,本篇文章是以QEMU/KVM作为背景的。

QEMU/KVM

virtio运用在多个虚拟化的产品中,QEMU/KVM就是其中之一。QEMU是由Fabrice Bellard提出的一个通过仿真来虚拟化的项目。完整地实现了处理器虚拟化、内存虚拟化以及I/O设备。但是由于采用了仿真方法,虚拟化的性能比较差。而KVM是由Avi Kivity提出的,一经提出便迅速进入Linux主线,它采用的是半虚拟化的方法。只提供处理器虚拟化和内存虚拟化,不提供I/O虚拟化。既然QEMU功能全面而性能薄弱,而KVM性能强劲但是功能薄弱,一个很自然的想法是将两者结合在一起。QEMU/KVM就是让KVM处理处理器虚拟化和内存虚拟化,运行在内核态;让QEMU处理I/O虚拟化,运行在用户态。QEMU/KVM的架构如下图所示:

其中,QEMU通过ioctl与/dev/kvm交互;1个QEMU进程可以启动1个VM;1个VM里面的vcpu是由一个线程负责的。

virtio基本知识

在了解了virtio在虚拟化整个框架的位置后,下面介绍一下virtio的基本知识及必要性,本小节内容和图片参考[4]。

virtio是一个半虚拟化的I/O方法,是由IBM的Rusty Russell在2007年提出的。通过仿真虚拟化I/O的方法适用性比较强,但是由于经历了Guest OS、QEMU、Host OS多个阶段,所以会产生多次VM Exit和VM Entry,性能比较弱,而I/O成为了整个系统的瓶颈,这也是迫切需要virtio的原因。

virtio具体架构如下所示:

主要的改进在于,不直接采用现有的硬件驱动,淘汰了客户机驱动,设备模拟层,VMM中的硬件驱动三层架构,而是采用一种更为高级别的抽象,减少了多层沟通的开销。virtio相比于仿真,不再是全部通过中断的方法通知读取数据,而是通过更轻量化的事件机制异步通信;采用Vring的方法在前后端中共享内存;也不会再完整地模拟寄存器的行为来控制设备和驱动,因为内存的性能无法和寄存器相比,失去了模拟的意义,直接使用内存中的一块区域;也不需要每一次都进入到KVM module里面,减少了VM-exit的次数,降低了开销。

其中Virtio Device是QEMU模拟出来的设备,是virtio的backend,负责操作物理设备。Virtio Driver是Guest OS中的驱动,是virtio的frontend,例如Virtio-net,Virtio-blk,负责发送Guest OS的I/O请求。前端和后端设备之间是通过Virtqueue来实现的,virtqueue是数据操作的接口,它具体是通过vring来实现的,但是只会暴露给外界virtqueue的接口。前后端通信是通过Notification机制来进行的。

virtio网络设备是virtio架构中的一员,负责虚拟化网卡的工作。主要由前端驱动和后端设备组成,随着后端设备放置位置的不同,性能也会不同。本文介绍的是virtio-net,它的后端设备放在qemu进程里面。

virtio-net相关内容

下面具体解析virtio-net的相关内容,本小节内容和图片参考[5],[6],[7],[8],[9],[10],[11],[12]。virtio-net主要涉及内容有Virtio Device,Virtio Driver,Virtqueue,Notification,如下图所示:

他们的作用已经在virtio基本知识小节介绍过了,下面说明各个部分的具体情况,来看一下virtio-net是如何起作用的.

Virtio Device

首先来看Virtio Device。virtio-net后端设备负责和tap虚拟网卡通信,进而控制实际硬件。它是在qemu中利用面向对象的思想实现的。virtio设备和相关总线关系如下图所示:

而virtio设备的挂载过程如下:首先创建一个virtio-net-PCI代理设备,挂载到PCI总线上;再创立一个virtio总线,挂载到上面的代码设备上;然后在virtio总线上挂载virtio设备,如virtio_net,virtio_blk等设备。

Virtio Driver

接着是Virtio Driver前端驱动,主要实现代码在linux_source_code/drivers/net/virtio_net.c中。

在讲Virtio Driver之前,首先需要了解一下Linux设备驱动的模型,这是整个driver实现的基础。设备模型是在Linux2.6提出来的,用Class,Bus,Device,和Device driver来抽象设备。Bus上挂载有Device设备和Device driver;Bus和Device、Device driver的拓扑关系可以用来描述一个设备树的依赖关系;热插拔设备的Driver提前注册在Bus上,当一个设备插入Bus时注册Device,此时通过match函数来和Bus上的Driver来匹配,来执行相关函数。

首先介绍一下virtio_bus,bus的结构如下图所示:

virtio_bus是一个bus_type的实例,和pci_bus_type并列;virtio_net驱动其实是一分为二,它利用pci_driver和virtio_driver两个driver实现,所以virtio_pci_driver和virtio_net_driver分别挂载在pci和virtio总线上;他们的device也是类似的结构,device之间通过一个virtio_pci_device连接。在初始化时,virtio设备会先在pci bus上完成初始化,然后在virtio bus上完成初始化。

然后介绍virtio-net driver和device的初始化具体过程:

  • pci和virtio侧的驱动是在系统初始化的时候都注册好了;
  • virtio_net设备是挂载在pci总线上的,在pci_scan_device枚举设备的过程中,发现设备后会注册一个pci_dev;
  • 然后会先引发pci侧的driver和device的match,最后调用register_virtio_device,注册virtio侧device;
  • 注册了virtio侧的device后,会引发virtio侧的match,进而调用driver的virtnet_probe函数,创建virtio-net;

Virtqueue

virtqueue是Virtio Device和Virtio Driver沟通的方式,外界使用这个接口进行通信,而内部则是采用vring的方法实现的;每一个virtio设备可能有多个virtqueue,而在virtio-net中最少有两条virtqueue,分别用来收发信息;也可以配置多条收发队列。

virtqueue具体的数据是vring来管理,vring用了三个数据结构用来管理内存:

  • Vring_desc是描述符表,用于记录所有的内存区域,每一项描述符表指向一块内存。指向的内存是通过scatterlist来组织的,然后会把内存地址放到描述符表的addr项中,长度用len表示。
  • Vring_avail的id是表示可用的描述区域,用于Guest向Host标志数据区域可用。
  • Vring_used的id表示已用的描述符区域,用于Host向Guest标志数据区域可用。

这里解释一下avail和used的意思,两者是站在Host OS的角度来说的。avail就是说现在Host可以读取,意味着Guest OS向内放入了数据而used是说Host使用完成,告知Guest现在可以读取。

最后来看一下virtqueue的通信过程。Guest OS读取数据的流程如下:

①表示virtioDriver从Used队列中获取一个Buffer的位置,并将Data拷贝到Buffer中,用于传送(还可以从符号表分配一个Buffer),然后更新Avail队列中的描述符索引值;②表示Driver通知Device来取数据;③表示Device从Avail队列中获取到描述符索引值,并将描述符索引对应的地址中的数据取出来;最后Device更新Used队列中的描述符索引,为下一次driver获取描述符表做准备;④表示Device通知Driver数据已经取完了。发送过程和接受过程原理相似,方向相反,这里就不再赘述了。

Notification

Notification是前后端通信的机制。guest向host发送通知,采用的是ioeventfd,通知的这个过程叫kick;host向guest发送通知,采用的是irqfd。两者都依赖于内核中的一个功能eventfd。KVM相当于一个代理设备,在QEMU和Guest OS之间互相通知对方,示意图如下:

参考文献

[1] virtio: Towards a De-Facto Standard For Virtual I/O Devices

[2] 深入浅出系统虚拟化:原理与实践

[3] 术道经纬知乎专栏虚拟化部分

[4] virtio网络演化历史

[5] LoyenWang公众号虚拟化专栏

[6] 深度探索Linux系统虚拟化 原理与实现

[7] QEMU/KVM源码解析与应用

[8] 系统虚拟化原理与实现

[9] 蜗窝科技统一设备模型专栏

[10] Linux设备驱动开发详解

[11] Virtio----Vring Introduce

[12] virtio-net 实现机制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/52675.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

非对称风险模型

推荐模型:非对称性风险,让自己置身于一个好结果比坏结果影响大得多的环境中 比如投资,将85%~90%投入到极低风险的资产中(国债),来享受确定性收益;剩下的投入的10%~15%投入到极高风险资产中&…

使用FeatureTask多线程优化in,提高查询速度

场景是这样的:使用in查询数据的时候,in的数量越多,效率越低,所以一个优化的思路是,缩小in查询的数量,用多线程的方式查询缩小数量后in的sql,并行查询。 直接上代码: public List&l…

Linux零基础入门(四)Linux实用操作

Linux零基础入门(四)Linux实用操作前言Linux实用操作一 各类小技巧(快捷键)1 ctrl c 强制停止2 ctrl d 退出或登出3 历史命令搜索4 光标移动快捷键5 清屏二 软件安装1 Linux系统的应用商店2 yum命令3 apt命令 - 扩展三 systemct…

全光谱台灯对孩子有伤害吗?儿童用台灯的好处和坏处是什么

全光谱台灯是指灯光色谱丰富度与太阳光一般全面的台灯,这样的灯光照射下的任何物体,不但颜色丰富多彩,而且极其真实,无限接近太阳光下的真实色彩,对人眼舒适度有巨大的提升,所以全光谱台灯不但对孩子无害&a…

5G无线技术基础自学系列 | MU-MIMO原理

素材来源:《5G无线网络规划与优化》 一边学习一边整理内容,并与大家分享,侵权即删,谢谢支持! 附上汇总贴:5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 MU-MIMO是指多个用户在上下行数据传输时…

算法实操:Python代码实现直插排序(含有序在前有序在后)

【学习的细节是欢悦的历程】Python 官网:https://www.python.org/ Free:大咖免费“圣经”教程《 python 完全自学教程》,不仅仅是基础那么简单…… 自学并不是什么神秘的东西,一个人一辈子自学的时间总是比在学校学习的时间长&a…

[附源码]SSM计算机毕业设计音乐网站JAVA

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

用结构体解决实际问题中构造数据类型

用结构体解决实际问题中构造数据类型。 实验内容 (1)题目:编写input()和output()函数输入,输出5个学生的数据记录。学生数据定义如下: #define N 5struct student{ char num[6];char name[8];int score[4];} stu[N];…

anaconda安装paddle(安装CUDA,CUDNN)

前言 为什么会写这样一篇呢,应该早晚会用到paddlelite,所以paddle还是要学的,与其在飞桨平台上跑,不如在自己电脑上跑。我以为安装paddle只需要三行代码: # 打开Anaconda Prompt conda create -n paddle python3.9 c…

C++异常及异常优缺点

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸 文章目录一、C语言传统的处理错误的方式二、C异常1.throw catch2.异常的抛出和捕获3.异常的抛出和捕获原则4.函数调用链 异常 栈展开匹配原则5.异常的…

微信小程序怎样开发?【小程序开发】

说到微信小程序,很多公司企业商家都会有自己的微信小程序,已经是他们的标配了。那么还没有自己的微信小程序的小伙伴,也在筹备着开发小程序。那么微信小程序怎样开发的呢,今天就教大家一个比较简单的开发方法。 微信小程序怎样开…

Linux进程通信之共享内存

一、共享内存之原理 1.是在物理内存中开辟了一片空间; 2.不同的进程通过页表将物理内存空间映射到自己的进程虚拟地址空间之中 3.不同的进程可以通过操作自己的虚拟地址空间中的虚拟地址去操作共享内存(物理地址) 共享内存是最快的进程之间…

为什么计算机中的负数要用补码表示?

本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 前言 大家好,我是小彭。 在前面的文章里,我们聊到了计算机的冯诺依曼架构的 3 个基本原则。其中第 1 个原则是计算机中所有信息都是采用二进制格式的编…

【在Spring MVC框架中,关于限制请求方式】

目录 1.关于限制请求方式 2. 附:关于GET和POST请求方式 1.关于限制请求方式 在Spring MVC框架中,RequestMapping注解的主要作用是配置请求路径,除此以外,还可以配置请求方式,例如: RequestMapping(value…

【Linux常见指令1】

目录:前言常用指令ls指令whoami && pwdcdtouch (触摸)mkdir (make directory)rmdir && rm (remove)mv(move 移动)cp(copy 拷贝)stat (统计)nanoechogccman(重要&…

如何在一台服务器同一个端口运行多个pgbouncer

PGbouncer是Postgresql数据库最常用的一款连接池软件,但是它是单进程的,所以只能占用一颗CPU资源,会造成CPU资源的浪费。PGbouncer有方法在同一台服务器的同一个端口运行多个进程实例,可以让资源得到充分利用。 先看下一个pgbounc…

【愚公系列】2022年12月 使用win11系统自带SSH,远程控制VMware中Liunx虚拟机系统

文章目录前言1.cpolar简介2.cpolar功能一、使用win11系统自带SSH,远程控制VMware中Liunx虚拟机系统1.注册cpolar账号2.下载最新版Ubuntu系统3.Ubuntu系统安装curl4.Ubuntu系统安装cpolar5.Ubuntu开启SSH6.WIN11测试SSH总结前言 身为开发人员,虚拟化系统…

Java基础之《netty(6)—NIO快速入门》

一、案例 1、编写一个NIO入门案例,实现服务器端和客户端之间的数据简单通讯(非阻塞) 2、目的:理解NIO非阻塞网络编程机制 3、代码 NIOServer.java package netty.niostart;import java.io.IOException; import java.net.InetSoc…

死锁问题【javaEE初阶】

什么是死锁? 所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 因此我们举个例子来描述,如果此时有一个线程A&…

【pen200-lab】10.11.1.217

pen200-lab 学习笔记 【pen200-lab】10.11.1.217 🔥系列专栏:pen200-lab 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 📆首发时间:🌴2022年11月30日🌴 🍭作…