KVM计算虚拟化原理,偏基础
本文基于网上的资料整理而成。
第一章 服务器虚拟化概述
1.1 为什么需要服务器虚拟化
如果物理机上只部署一种业务,资源利用率太低,不利于节约成本。如果说生产区域需要使用物理机来保证稳定性,对于开发测试区使用虚拟机不但可以节约有限的物理机资源,还可以快速上线。
1.2 虚拟化发展历史
- 提出概念:1959年6月提出,在国际信息处理大会上发表的《大型高速计算机中的时间共享》论文中提出
- 开发技术:20世纪60年代开始,IBM操作系统虚拟化技术应用在了大型机和小型机上
- 蓬勃发展:20世纪90年代,VMware公司率先实现了X86架构上的虚拟化,于1999年推出了x86平台上的第一款虚拟化商业软件VMware workstation。
- 群雄逐鹿:更多的厂商加入了虚拟化技术的队伍
第二章 服务器虚拟化
服务器虚拟化主要有两种方式:
- 硬件虚拟化:通过Hypervisor层虚拟出硬件系统环境,将硬件系统CPU发出的指令经过处理后传到物理CPU上。
硬件虚拟化的关键在于Hypervisor层。
所谓Hypervisor层就是在物理服务器和操作系统中间运行的软件层,可以对模拟硬件系统,将程序对这个硬件系统CPU发送的指令经过处理之后,加以虚拟传到物理CPU上。同时它可以协调访问服务器上的物理设备和虚拟机,也叫虚拟机监视器(VMM )。
- 容器:只是虚拟化出应用运行时的环境,是比较轻量的虚拟化,层次比较浅。
2.1 服务器虚拟化架构
- 裸金属架构:Hypervisor层直接运行在硬件系统上。典型例子是KVM。KVM其实就是Linux内核提供的虚拟化架构,可将内核直接充当Hypervisor,KVM一般需要处理器本身支持虚拟化扩展技术,如Intel VT等。KVM使用内核模块kvm.ko来实现核心虚拟化功能,但是只提供了CPU和内存的虚拟化,必须结合QEMU才能构成完整的虚拟化技术。
2.2 CPU虚拟化
进程的执行有两种状态
- 内核态:主要用于硬件访问,修改关键参数,
- 用户态:用户运行应用程序。
两种状态的权限不同,对硬件的访问必须在内核态,可以保证系统的可靠性,只给应用人员开放用户态,不会对OS的运行带来大的影响。避免系统被人为攻击。
OS内核数据和代码区应该与用户区完全隔离,也就是说程序可以看到的地址都是用户态地址,当程序执行系统调用的时候,进程会切入内核态进行内核访问,此时页表也需要切换到内核态的页表,带来的问题是性能比较差。因为页表在内存中,切换会带来性能的降低。
所以目前主流的OS的做法是将内核代码和数据区放到用户进程虚拟地址控制器的高位区,32bit系统放到3~4G,windows默认占用2~4G区,64bit系统也放在高位。这样带来的好处是,进程空间的内核区也被映射到物理内存区,进程的切换不会导致TLB中之前缓存的针对内核区页表失效,保证了性能。
其实进程是无法访问内核区,因为强行访问的话,页表条目有权限位(进程当前权限保存在寄存器的CPL字段,为Ring3,而内核页表的权限为Ring0,所以CPU会禁止访问。)
总结一下就是x86 架构提供四个特权级别给操作系统和应用程序来访问硬件。 Ring 是指 CPU 的运行级别,Ring 0是最高级别,Ring1次之,Ring2更次之……
- 内核需要直接访问硬件和内存,因此它的代码需要运行在最高运行级别 Ring0上,这样它可以使用特权指令比如控制中断、修改页表、访问设备等等。
- 应用程序的代码运行在最低运行级别上Ring3上,如何要访问磁盘,那就需要执行系统调用,此时CPU的运行级别会发生从ring3到ring0的切换,并跳转到系统调用对应的内核代码位置执行,这样内核就为你完成了设备访问,完成之后再从ring0返回ring3。这个过程也称作用户态和内核态的切换。
对于非虚拟化操作系统而言,应用程序和系统下发的普通指令都运行在用户级别指令中,只有特权指令运行在核心级别中,这样操作系统与应用解耦合。
那么,虚拟化在这里就遇到了一个难题,因为物理机OS是工作在 Ring0 的,虚拟机的操作系统就不能也在 Ring0 了,所以一些特权指令是没有执行权限的
CPU虚拟化的方法就是
- 特权解除:让Guest OS运行在用户级别,让hypervisor运行在核心级别,这样就解除了Guest OS的特权级别。
- 陷入模拟:运作在Guest OS的普通指令像过去一样运行,当运行到特权指令时,会产生异常并被hypervisor捕获。
那么难点在于:
- 如何模拟x86保护模式
- 如何拦截并执行虚拟机的Ring0指令。
解决方法如下
2.2.1 CPU虚拟化技术解决方法
- 全虚拟化:客户操作系统运行在 Ring 1,它在执行特权指令时,会触发异常,然后 hypervisor捕获这个异常,在异常里面做翻译,最后返回到客户操作系统内,客户操作系统认为自己的特权指令工作正常,继续运行。所以也叫二进制翻译技术(Binary Translate)。
但是这个性能损耗非常的大,简单的一条指令现在却要通过复杂的异常处理过程 - 半虚拟化:修改操作系统内核,替换掉不能虚拟化的指令,通过超级调用(hypercall)直接和底层的虚拟化层hypervisor来通讯,
相对于完全虚拟化性能更高,因为省去了翻译的过程。但是需要对Guest OS进行修改,应用场景不多。 - 硬件辅助虚拟化: 2005年后,CPU厂商Intel 和 AMD 开始支持虚拟化了。 Intel 引入了 Intel-VT (Virtualization Technology)技术
主要的实现方式是增加了一个VMX non-root操作模式,运行VM时,客户机OS运行在non-root模式,依然有Ring0~ring3等级别
当运行特权指令时或者发生中断的时候,通过VM_EXIT就可以切换到root模式,拦截VM对虚拟硬件的访问。执行完毕,通过VM_ENTRY回到non-root即可。
这种技术主要代表为intel VT-X,AMD的AMD-V
全虚拟化 |
半虚拟化 |
硬件辅助虚拟化 |
实现技术 |
BT和直接执行 |
Hypercall |
客户操作系统修改/兼容性 |
无需修改客户操作系统,最佳兼容性 |
客户操作系统需要修改来支持hypercall,因此它不能运行在物理硬件本身或其他的hypervisor上,兼容性差,不支持Windows |
性能 |
差 |
好。半虚拟化下CPU性能开销几乎为0,虚机的性能接近于物理机。 |
应用厂商 |
VMware Workstation/QEMU/Virtual PC |
Xen |
KVM 是基于CPU 辅助的全虚拟化方案,它需要CPU虚拟化特性的支持。
总结:
2.3 内存虚拟化原理
内存虚拟化指的是共享物理系统内存,动态分配给虚拟机。虚拟机的内存虚拟化很象虚拟内存方式
虚拟内存是计算机系统内存管理的一种技术,目的是让应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间)。其实就是操作系统将内存资源的虚拟化,屏蔽了内存调用的细节,对应用程序而言,不需要关心内存访问的细节,可以把内存当作线性的内存池。,
x86 CPU 都包括了一个称为内存管理的模块MMU(Memory Management Unit)和 TLB(Translation Lookaside Buffer),通过MMU和TLB来优化虚拟内存的性能。
OS将内存按照4KB为单位进行分页,形成虚拟地址和物理地址的映射表。如果OS在物理机上运行,只要OS提供这个页表,MMU会在访存时自动做虚拟地址(Virtual address, VA)到物理地址(Physical address, PA)的转化。
但是如果虚拟机上运行OS,Guest OS经过地址转化到的“物理地址”实际上是QEMU的逻辑地址,因此还需要使用软件将其转化为真实物理内存地址
对于OS运行在物理机上的情况
如果进程访问内存的时候,发现映射表中还没有物理内存进行对应。如下图
此时MMU向CPU发出缺页中断,操作系统会根据页表中的外存地址,在外存中找到所缺的一页,将其调入内存。同时更新页表的映射关系。下一次访问的时候可以直接命中物理内存。
对于OS在虚拟机中的情况,过程就要复杂很多。
对于虚拟机内的进程的转换,需要进行两次转换。也就是说首先将应用的逻辑地址转换为虚拟机的物理地址,而这实际是QEMU进程的逻辑地址,所以要映射到实际内存的物理地址还需要做一次转换。
- VA:应用的虚拟地址
- PA:虚拟机物理地址,也是QEMU进程的逻辑地址
- MA:物理机的物理地址
可见,KVM 为了在一台机器上运行多个虚拟机,需要增加一个新的内存虚拟化层,也就是说,必须虚拟 MMU 来支持客户OS,实现 VA -> PA -> MA 的翻译。
客户操作系统继续控制虚拟地址到客户内存物理地址的映射 (VA -> PA),但是客户操作系统不能直接访问实际机器内存,因此VMM 需要负责映射客户物理内存到实际机器内存 (PA -> MA)。
VMM 内存虚拟化的实现方式:
- 软件方式:通过软件实现内存地址的翻译,比如 Shadow page table (影子页表)技术
- 硬件实现:基于 CPU 的辅助虚拟化功能,比如 AMD 的 NPT 和 Intel 的 EPT 技术
2.3.1 软件方式
影子页表(SPT,shadow page table):Hypervisor为虚拟机维护了一个虚拟机的虚拟地址到宿主机物理地址映射的的页表。也就是说,在原来的两层地址层次基础上加了一层伪物理地址层次,通过这张表可以将客户机虚拟地址和宿主机物理地址之间进行映射。
客户OS创建之后,Hypervisor创建其对应影子页表。刚开始影子页表是空的,此时任何客户OS的访存操作都会发生缺页中断,然后Hypervisor捕获缺页异常
通过两次地址映射转换获得虚拟机虚拟地址和物理机物理地址的映射关系,写入影子页表,逐步完成所有虚拟地址到宿主机机器地址的映射。
代价是需要保持虚拟机的页表和宿主机的影子页表的同步。
2.3.2 通过INTEL EPT技术来实现
KVM 中,虚机的物理内存即为 qemu-kvm 进程所占用的内存空间。KVM 使用 CPU 辅助的内存虚拟化方式。在 Intel 和 AMD 平台,其内存虚拟化的实现方式分别为:
- AMD 平台上的 NPT (Nested Page Tables) 技术
- Intel 平台上的 EPT (Extended Page Tables)技术
EPT 和 NPT采用类似的原理,都是作为 CPU 中新的一层,通过硬件用来将客户机的物理地址翻译为主机的物理地址。也就是说Guest OS完成虚拟机虚拟地址-->虚拟机物理地址第一层转化,硬件同时完成虚拟机物理地址到物理机物理地址这第二层转化。第二层转换对Guest OS来说是透明的,Guest OS访问内存时和在物理机运行时是相同的。这种方式又叫做内存辅助虚拟化。
所以内存辅助虚拟化就是直接用硬件来实现虚拟机的物理地址到宿主机的物理地址的一步到位映射。VMM不用再保留一份 SPT (Shadow Page Table),通过EPT技术,不再需要同步两个页表,虚拟机内部的切换也不需要qemu进程切换,所需要的是只是两次页表查找,而且是通过硬件来完成的,性能损耗低。
流程如下:
- VM中的应用发现页没有分片,MMU发起中断,从虚拟机的物理地址(QEMU的逻辑地址)中分配一页,然后更新页表。
- 此时虚拟机页的物理地址还没对应物理内存的地址,所以触发了qemu进程在宿主机的page fault。宿主机内核分配内存页,并更新页表。
- 下次访问就可以借助EPT来进行,只需要查两次表即可。
2.4 KVM其他内存管理技术
2.4.1 KSM (Kernel SamePage Merging 或者 Kernel Shared Memory)
KSM 是内核中的守护进程(称为 ksmd),它会定期进行页面扫描,将副本页面进行合并,然后释放多余的页面。KVM使用KSM来减少多个相似的虚拟机的内存占用,提高内存的使用效率,在虚拟机使用相同镜像和操作系统时,效果更加明显。但是会增加内核开销,所以为了提升效率,可以将此特性关闭。
2.4.2 KVM Huge Page Backed Memory (巨页内存技术)
Intel 的 x86 CPU 通常使用4Kb内存页,当是经过配置,也能够使用巨页(huge page): (4MB on x86_32, 2MB on x86_64 and x86_32 PAE)使用巨页,KVM的虚拟机的页表将使用更少的内存,并且将提高CPU的效率。最高情况下,可以提高20%的效率!
2.5 IO虚拟化
- 模拟(完全虚拟):使用 QEMU 纯软件的方式来模拟 I/O 设备。使用一个Service VM来模拟真实硬件,性能很差。
客户机的设备驱动程序发起 I/O 请求操作请求,KVM会捕获此IO请求,然后放到IO共享页,同时用户空间的QEMU进程,QEMU模拟出本次IO操作,同样放到共享页中并同时KVM进行结果的取回。
注意:当客户机通过DMA (Direct Memory Access)访问大块I/O时,QEMU 模拟程序将不会把结果放进共享页中,而是通过内存映射的方式将结果直接写到客户机的内存中,然后通知KVM模块告诉客户机DMA操作已经完成。
- 半虚拟化: KVM/QEMU就采用这种模式,它在 Guest OS 内核中安装前端驱动 (Front-end driver)和在 QEMU 中实现后端驱动(Back-end)的方式。前后端驱动通过 vring (实现虚拟队列的环形缓冲区)直接通信,这就绕过了经过 KVM 内核模块的过程,提高了IO性能,相对于完全虚拟的模式, 省去了纯模拟模式下的异常捕获环节,Guest OS 可以和 QEMU 的 I/O 模块直接通信。
- IO-through:直接把机物理设备分配给虚拟机,不过需要硬件具备IO透传技术;,Intel 定义的 I/O 虚拟化技术成为 VT-d,AMD 的称为 AMD-V。
KVM 支持客户机以独占方式访问这个宿主机的 PCI/PCI-E 设备。通过硬件支持的 VT-d 技术将设备分给客户机后,在客户机看来,设备是物理上连接在PCI或者PCI-E总线上的
几乎所有的 PCI 和 PCI-E 设备都支持直接分配,除了显卡以外(显卡的特殊性在这里)。PCI Pass-through 需要硬件平台 Intel VT-d 或者 AMD IOMMU 的支持。这些特性必须在 BIOS 中被启用- 好处:减少了 VM-Exit 陷入到 Hypervisor 的过程,极大地提高了性能,可以达到几乎和原生系统一样的性能。而且VT-d 克服了 virtio 兼容性不好和 CPU 使用频率较高的问题。
- 不足:独占设备的话,无法实现设备的共享,成本提高。
- 不足的解决方案:(1)在一台物理宿主机上,仅少数 I/O 如网络性能要求较高的客户机使用 VT-d直接分配设备,其他的使用纯模拟或者 virtio 已达到多个客户机共享同一个设备的目的 (2)对于网络I/O的解决办法,可以选择 SR-IOV 是一个网卡产生多个独立的虚拟网卡,将每个虚拟网卡分配个一个客户机使用。
2.6 网卡虚拟化
VM发出的流量一般有两种
- 到物理机外部的设备,
- 到本地物理服务器上的虚拟机。
所以我们需要保证不同虚拟机流量的相互隔离,同时又要考虑物理设备内虚拟机的互联互通。
解决方法:
对于对物理机外部的流量,给每个VM分配一个专用通道,共享物理网卡资源。
主要有如下几种模式:
- Bridge桥接模式:把物理主机上的网卡当交换机,然后虚拟出一个Bridge来接收发往物理机的包。
- isolation mode:仅guest OS之间通信;不与外部网络和宿主机通信。
- routed mode:与外部主机通信,通过静态路由使得各Guest OS 的流量需经过物理网卡
- nat:地址转换;在虚拟网卡和物理网卡之间建立一个nat转发服务器;对数据包进行源地址转换。
对内部流量:
- 在hypervisor上建立virtual switch,不过会耗费CPU资源,而且存在较大安全隐患。(intel的VT-c用VMDq技术使网卡芯片处理一些vswitch的工作,同时用vFW来保障安全)
- 可以先让流量出服务器通过安全设备区域进行数据清洗以后再回来。主流方式利用硬件SR-IOV对VM流量进行识别和处理
总结
2.7 Hypervisor层的虚拟化实现
操作系统是用户和物理机的接口,也是应用和物理硬件的接口。核心功能在于任务调度和硬件抽象。
不同操作系统的最大不同在于内核。
- 单内核:内核所有的功能代码全部都运行在同一个内核空间内,优点是性能性能很高,缺点是设计复杂,稳定性不够好;
- 微内核:类似C/S服务模式,只有最基础的代码会运行于内核空间,其他的都运行于用户空间,优点是稳定性高,缺点性能较低;
- 混合内核:性能与稳定的妥协产物,完全由设计者进行用户自定义;
- 外内核:比微内核更加极端,连硬件抽象工作都交给用户空间,内核只需要确保应用程序访问硬件资源时,硬件是空闲的
2.7.1 主流的Hypervisor总结
Hypervisor的类别 |
KVM |
Xen |
Hyper-v |
ESX/ESXi |
内核类型 |
Linux |
Linux(修改)&Nemesis |
Windows&Hyper-V |
Linux&VMernel |
内核 |
单核 |
外内核 |
外内核 |
混合内核 |
I/O虚拟化方式 |
类Service VM Model |
Service VM Model |
Service VM Model |
Monolithic Model |
特点 |
集成在Linux内核中 |
有被KVM取代之势,性能低于KVM |
捆绑Windows Server |
技术积累深厚,Vmkernel是核心竞争力 |