注:文中图片直接借用大佬文章中的图片,链接见文末。
一、虚拟化技术简介
1. 什么是虚拟化技术
在一个物理机(宿主机)上安装一个操作系统,操作系统上安装一个虚拟化软件,在虚拟化软件上创建出多个虚拟机。虚拟机发起系统调用等特权指令的操作时会被虚拟化软件接收,经过虚拟化软件处理后交由宿主机实现系统调用。这样就实现了在一个物理机上运行多个虚拟机,而这个实现的过程就是虚拟化,支持这种虚拟化的技术就是虚拟化技术。
2. 虚拟化模式
x86架构CPU提供4个运行级别给操作系统和应用程序来访问硬件,权限级别由高到低分别为:Ring0、Ring1、Ring2、Ring3,操作系统(内核)的代码运行在最高权限级别Ring0,应用程序的代码运行在最低权限级别Ring3。应用程序需要访问硬件时,执行系统调用,CPU级别从Ring3切换到Ring0,跳转到系统调用对应的内核代码位置执行,完成设备访问,然后再从Ring0切换回Ring3,继续执行应用程序代码。这个过程称作用户态和内核态的切换。
那么,虚拟机应该运行在哪个CPU运行级别呢?因为宿主机操作系统是工作在Ring0的,所以虚拟机操作系统就不能也运行在Ring0了,但是这样一来,虚拟机操作系统就没有足够的权限执行所有的指令了,当执行一些特权指令时是会报错的。那怎么办呢?虚拟机管理程序(VMM,Virtual Machine Manager)就是来解决这一问题的。虚拟机通过VMM实现其CPU对硬件的访问,根据其原理不同分为两种模式:半虚拟化模式和全虚拟化模式,其中全虚拟化模式又分为基于二进制翻译的全虚拟化和硬件辅助的全虚拟化。
(1)半虚拟化
半虚拟化也叫超虚拟化、操作系统辅助虚拟化。
半虚拟化的思想:修改虚拟机操作系统内核,替换掉不能虚拟化的指令,通过超级调用(hypercall)直接和底层的虚拟化层(hypervisor)通讯。
半虚拟化的特点:性能好,效率佳,但修改操作系统内核难度大,且只支持虚拟化Linux,无法虚拟化Windows。
(2)基于二进制翻译的全虚拟化
将虚拟机操作系统放在Ring1运行,它在执行特权指令时,会触发异常(CPU的机制,执行没权限的指令会触发异常),然后VMM捕获这个异常,在异常里面做翻译,模拟,最后返回到虚拟机操作系统内,虚拟机操作系统认为自己的特权指令能正常工作,继续运行。
特点:性能损耗非常大,效率低。
(3)硬件辅助的全虚拟化
支持硬件辅助虚拟化技术的CPU具有两种操作模式:root模式和non-root模式,且每种操作模式下都有独立的Ring0 - Ring3四个运行级别。VMM运行在root模式Ring0,而虚拟机操作系统运行在non-root模式Ring0。当虚拟机操作系统运行遇到需要VMM处理的事件时,则从non-root模式切换到root模式,VMM处理完成以后,再切回non-root模式。
特点:免去了基于二进制翻译的全虚拟化“捕获异常-翻译-模拟”这一耗费性能的过程,又不需要修改虚拟机操作系统内核;随着CPU厂商支持虚拟化力度的加大,性能逐渐逼近半虚拟化,是未来发展的趋势。
二、QEMU-KVM基本原理
1. QEMU
QEMU:Quick Emulator,是一个纯软件实现的虚拟化系统,代码中包含了整套的虚拟机实现,包括CPU虚拟化、内存虚拟化,以及其他虚拟设备的模拟,比如网卡、显卡、硬盘等。
QEMU可作为一个宿主机的VMM,通过动态二进制转换来模拟CPU,并提供一系列的硬件模型,使虚拟机操作系统认为自己在和硬件打交道,其实是同QEMU模拟出来的硬件打交道,QEMU再将这些指令翻译给真正的硬件进行操作。
由于所有指令都需要经过QEMU来翻译,所以性能比较差。
2. KVM
KVM:Kernel-based Virtual Machine,是基于Linux内核的开源虚拟化解决方案,从Linux kernel2.6.20开始被引入Linux内核代码中,使用此版本或更高版本内核的Linux,则可直接使用KVM。
KVM是依赖于硬件辅助虚拟化技术的全虚拟化解决方案,其基本思想是在Linux内核的基础上添加虚拟机管理模块,重用Linux内核中已完善和成熟的机制和模块,比如进程调度、内存管理、IO管理等,使之成为一个可以支持运行虚拟机的hypervisor(虚拟机监控程序)。
KVM只提供了CPU和内存管理的虚拟化。
3. QEMU-KVM
因为KVM只提供了CPU和内存管理的虚拟化,所以还需要借助于其他工具来模拟其他设备,才能构成一个完整的虚拟化技术。KVM的开发者选择了QEMU,对其代码进行修改,与KVM结合,形成QEMU-KVM,KVM负责CPU和内存的虚拟化,QEMU则负责模拟IO设备等硬件。
架构:
说明:
(1)QEMU-KVM属于硬件辅助的全虚拟化技术,CPU具有root和non-root两种操作模式。VMM(KVM)和宿主机内核运行于root模式下的Ring0,QEMU-KVM运行于root模式下的Ring3;虚拟机内核运行于non-root模式下的Ring0,虚拟机中的用户态程序运行于non-root模式下的Ring3。
(2)一个KVM虚拟机就是一个QEMU-KVM进程,与常规Linux进程无异,与其他Linux进程一样被Linux进程调度器调度。KVM虚拟机的CPU作为线程运行在QEMU-KVM进程的上下文中,KVM虚拟机的内存则是QEMU-KVM进程地址空间的一部分。
(3)KVM本身不执行任何硬件模拟,IO设备等硬件由QEMU-KVM进行模拟。
(4)KVM向上层提供/dev/kvm接口,/dev/kvm是一个标准的字符设备,通过ioctl接口控制;QEMU-KVM通过调用/dev/kvm设备的ioctl接口,对虚拟机进行相关控制,比如创建虚拟机、创建vCPU、运行虚拟机等。(QEMU-KVM通过ioctl和/dev/kvm与KVM内核模块交互)
运行视图:
流程描述:
(1)运行在用户态的Qemu-kvm通过ioctl系统调用操作/dev/kvm字符设备,创建VM和vCPU。
(2)KVM内核模块负责相关数据结构的创建和初始化,然后返回用户态。
(3)Qemu-kvm通过ioctl调用运行vCPU,即调度相应的虚拟机运行。
(4)KVM内核进行相关处理后,执行VMLAUNCH指令,通过VM-Entry进入虚拟机操作系统运行,虚拟机操作系统运行于non-root模式下。
(5)虚拟机操作系统执行相应的虚拟机代码,有权限的指令可直接在物理CPU上运行。
(6)当虚拟机操作系统运行遇到无权限指令、发生外部中断、或虚拟机操作系统发生内部异常时,暂停虚拟机操作系统的运行,将产生VM-Exit退出non-root模式进行异常处理,并将相关信息记录到VMCS结构中。
(7)VM-Exit使CPU退回到root模式下,由KVM内核读取VMCS结构判断VM-Exit的原因。
(8)如果是IO操作或是其他外设指令,则返回用户态Qemu-kvm(即root模式下的Ring3),由Qemu-kvm完成对相关指令的模拟。
(9)如果不是,则由KVM自行处理。
(10)处理完成以后,重新VM-Entry进入到虚拟机操作系统中运行。
4. Libvirt
Libvirt是C语言编写的用于对KVM虚拟机进行管理的工具和应用程序接口(API),它屏蔽了底层各种Hypervisor的细节与差异,向上层提供一个统一、稳定的接口,在一些高级编程语言中已有Libvirt的程序库可以直接使用。
Libvirt主要由三部分组成:
(1)应用程序编程接口库
(2)守护进程:libvirtd
(3)默认的命令行管理工具:virsh
调用链:
virsh ——> libvirtd ——> qemu-kvm ——> /dev/kvm ——> KVM
架构: