Mastering KVM Virtualization

Mastering KVM Virtualization读书笔记第一章——理解 Linux 虚拟化

概述

第一章主要包含以下 5 个内容:

  • Linux 虚拟化及基本概念;
  • 为什么选择 Linux 虚拟化;
  • Hypervisor/VMM;
  • Linux 虚拟化在云中向你提供什么;
  • 公有云和私有云;

    什么是虚拟化

书中将virtual解释为“硬件环境不是真实的”。在虚拟化中将物理硬件功能复制出来并将他们提供给操作系统(Guest OS)。将这种创建虚拟硬件环境的技术叫做virtualization(虚拟化技术)。运行虚拟化软件(可能是 hypervisor 或者是 VMM)的物理硬件被称为 host,安装在虚拟化软件层之上的虚拟机被称为 guest。

以上的解释很好地概括了虚拟化的特征,但是除了常规的虚拟 PC 机这种虚拟化外,虚拟化还有更广的应用和更深的理解。jvm 就是一个很好的例子,我们知道 Java 程序编译成的字节码是无法直接运行在机器上的,字节码运行在 jvm 上,而 jvm 正是虚拟化技术的一种应用,jvm 抽象出一台可以运行 java 字节码的虚拟机,从而运行 Java 编译后的程序。因此只要是能够运行 jvm 虚拟机的设备就能够使用同一套 Java 代码,这也正是 Java 拥有优秀可移植性的原因,同时也是 Java 程序性能较差的原因。

虚拟化(virtualization)通常容易和模拟/仿真(emulation)搞混(但是如果你用书中对虚拟化的解释来理解,virtualization 似乎能包含 emulation)。

个人理解,emulation 是对硬件寄存器级别的模仿,更多的体现在异构平台转换指令集的操作上,例如在 x86 平台上运行一个 arm 设备,可以认为是 emulation。对于 virtualization 更倾向于同一架构平台的虚拟机,对指令集不做翻译,而是通过划分硬件资源(例如将 cpu 分为多个 vCPU 等)来提供硬件抽象。因此 emulation 相对 virtualization 效率会更差。对于 jvm 明明是通过翻译字节码工作,却被称为 java 虚拟机这件事我也感到挺疑惑的。

QEMU 本身是一款 emulator,但是它通过 KVM 也可以做到 virtualization。关键在于它选择的 accelerator 是 TCG 还是 KVM。

为什么使用 Linux 虚拟化

虚拟化在 Linux 上首次出现是以 UML(User-mode Linux)的形式,发展到现在已经有了很多成熟的虚拟化解决方案了,例如:KVM,QEMU,Xen 和 VirtualBox。

在 Linux 下许多虚拟化解决方案都是开源的,下面就是巴拉巴拉讲一些开源的好处,略过。

虚拟化的分类

简单地来说,虚拟化就是虚拟一些硬件,例如:网络、存储、应用、I/O 存取等,因此虚拟化能够应用在以上任意组件中:

  • SDN(Software-Defined Networking)软件定义网络,即网络虚拟化;
  • SDS(Software-Defined Storage)软件定义存储,存储虚拟化;
  • 应用流、远程桌面都属于应用虚拟化。

SDN 我听说过的有一个 Open vSwitch,SDS 有 Ceph。

本书主要聚焦在基于 hypervisor 的软件虚拟化,即平台虚拟化。在 host 和 guest 之间的 hypervisor/VMM。

虚拟化的优点

  • 集中服务,节约电力和管理成本。
  • 服务孤立,当我们需要跑两个互相不影响的应用时,一般我们要跑在两台独立的设备上,如果用虚拟化,在一台设备上,我们就能保证服务孤立,内存隔离;
  • 服务快速供应,
  • 灾祸备份,
  • 动态负载均衡,
  • 快速的开发测试环境搭建,
  • 提高系统可靠性和安全性,

略。

操作系统虚拟化/分区

操作系统虚拟化技术运行在同一个物理 host 上服务并隔离多个不同的工作负载(guest)。请注意,这些工作负载独立地运行在同一种 OS 上。这使得一个物理服务器可以运行多个隔离的操作系统实例,称之为containers。如果我们叫它基于容器的虚拟化也是没错的。这种虚拟化的优势在于 host OS 不需要为不同的 OS 模拟系统调用接口。由于没有提供上述接口,在这种虚拟化类型上不能虚拟其他 OS。这是一个常见且很好理解的局限。Solaris containers,FreeBSD jails 和 Parallel’s OpenVZ 都属于此类虚拟化。使用这种方式的时候,所有工作负载都运行在同一个系统上,由内核提供进程隔离和资源管理。虽然所有容器都运行在同一个内核上,但是它们拥有自己的文件系统,进程,内存,设备等等。从另一个角度来说,混杂 Windows,Unix 和 Linux 的工作负载在一个物理主机上的就不属于这种虚拟化。这种技术的局限性被性能和效率带来的好处所抵消,因为一个操作系统就提供了所有的虚拟化环境,换而言之,在不同分区间切换讲更为高效。

其实这里讲的就是操作系统虚拟化(容器),近些年比较火爆的是 docker,但在此之前其实早就有很多解决方案了。在 Linux 上将系统资源进行隔离的一个重要组件就是 cgroup,在 libvirt 中也集成了部分操作系统虚拟化工具。

protection rings

在计算机科学中,存在各种保护域/特权环。这是一种用来在发生故障时保护数据和功能,提升容错度,避免恶意操作 ,提升计算机安全的设计方式。

如图所示,rings 从内到外编号,特权等级依次递减,ring0 是特权等级最高的并且直接与硬件(CPU、内存)交互。内存、I/O 端口、CPU 指令等资源就是通过这些 rings 来进行保护的。ring1 和 ring2 通常不怎么使用,通常系统只使用 ring0 和 ring3,即使硬件提供了更多的 cpu 模式。两个主要的 CPU 模式就是内核模式和用户模式,从操作系统的角度来看,ring0 就是内核模式,ring3 则是用户模式。

Linux 和 Windows 操作系统使用内核模式和用户模式,程序在用户模式下无法与外界交互(硬件),除非使用系统调用或者其它帮助,这是由于用户模式下对内存,I/O 端口和 cpu 访问限制导致的。系统内核运行在特权模式下,这意味着它们运行在 ring0 下。为了执行特定的功能,用户模式下的代码必须执行系统调用。

由于 hypervisor/VMM 都需要访问内存,cpu 和 I/O 端口,由于只有 ring0 的代码才能够执行这些操作,因此 hypervisor/VMM 需要运行在 ring0 并且在 kernel 旁边。没有硬件虚拟化支持,运行在 ring0 的 hypervisor/VMM 阻碍了 guestOS,guestOS 只能运行在 ring1。虚拟机中的 OS 同样也希望在察觉不到虚拟化层的情况下访问所有资源,为了达到这个目的,它必须在 VMM 上跑类似 ring0 的代码。由于同一时间只能够在 ring0 上运行一个 kernel,guestOS 必须运行在其他特权环上,或者经过修改后运行在用户模式。

这导致引入了两种称为完全虚拟化和半虚拟化的虚拟化方法。

全虚拟化

在全虚拟化中,特权指令被模拟以克服由于 OS 运行在 ring1 且 VMM 运行在 ring0 所产生的限制。全虚拟化基于二进制翻译技术,二进制翻译技术可以陷入或者虚拟某些敏感不可虚拟化的指令。通过二进制翻译技术,一些系统调用被解释并且动态重写。

二进制翻译(binary translation)是通过翻译二进制代码来仿真另一个指令集。这个技术也被用在仿真(emulation)上,比如在 QEMU 中就可以通过 TCG 来将 arm 的指令集动态翻译成 x86 的指令集。

但是在全虚拟化中,二进制翻译的目标指令集和源指令集为同一指令集,在这里翻译是为了将敏感指令替换成陷入(特权)指令。在全虚拟化中,一般只需要翻译捕捉敏感指令然后进行替换,不含敏感指令的部分可以直接执行。

注意区分敏感指令和特权指令,在 x86 架构下,特权指令都是敏感指令,但是少部分敏感指令不是特权指令,因此这部分敏感指令需要被替换成特权指令。VMM 会捕获(trap)guestOS 中发出的特权指令,由 VMM 处理并返回结果。少部分敏感指令是不会触发 trap 的,因此需要替换成特权指令。

二进制翻译需要消耗大量的性能,但是通过全虚拟化我们可以使用未被修改过的 guestOS。当 guest kernel 执行特权指令的时候,VMM 为它提供针对受保护 CPU 操作的仿真。

半虚拟化

与全虚拟化相比,半虚拟化需要修改 guestOS,以便允许其指令允许在 ring0。换句话来说,我们需要修改 OS,通过“后端”(超调用)路径来在 VMM/hyperviosr 和 guest 之间进行沟通。

半虚拟化和全虚拟化要解决的问题是一样的,都是要考虑如何替换敏感指令,如何执行特权指令。半虚拟化采用的方式是直接修改 guestOS,将敏感指令操作替换为对 VMM 的超调用(hypercall)。

hypercalls 陷入到 VMM 中代替 guest kernel 执行这个任务,由于 guest kernel 拥有通过 hypercalls 直接联系 VMM 的能力,半虚拟化相较于全虚拟化拥有更优秀的性能表现。

分析全虚拟化和半虚拟化的异同:

同:

  • 全虚拟化和半虚拟化都是为了解决敏感指令的问题;

异:

  • 全虚拟化不修改 guestOS,捕获 guestOS 的敏感指令并翻译成特权指令,CPU 在非 ring0 时执行这些翻译后的特权指令陷入异常,该异常由 VMM 进行处理,在处理这些异常的时候,VMM 替 guestOS 完成特权指令操作,并返回操作数;
  • 半虚拟化修改 guestOS,它直接将敏感指令替换为对 VMM 的 hypercall,这样就减少了捕获、陷入的过程,直接由 VMM 处理特权指令。

硬件辅助虚拟化

意识到虚拟化的问题,Intel 和 AMD 各自发展出了基于 x86 扩展的 VT-x 和 AMD-V。硬件辅助虚拟化采用硬件辅助的方式对全虚拟化进行加速。Intel 和 AMD 提供了 VT 和 SVM 作为 IA-32 的指令集扩展。这些扩展允许 VMM 将 guestOS 在低特权 ring 上运行内核模式。硬件辅助虚拟化不仅提供了新的指令集,同样提供了一个新的特权级 ring-1。VMM/hypervisor 运行在 ring-1,guestOS 运行在 ring0。详细的硬件辅助虚拟化(以 KVM 为例)实现在书中剩余部分。

硬件辅助虚拟化其实是通过硬件的方式实现了将敏感指令陷入到 VMM 的工作,从而提高了效率。

hypervisor 和 VMM

VMM/hypervisor 是用来管理控制虚拟机的软件。它负责确保不同的虚拟化任务,例如:提供虚拟硬件、虚拟机生命周期管理、虚拟机迁移、实时分配资源、为虚拟机管理器设置策略等等。同时也要负责有效地控制物理平台资源,例如内存地址翻译和 I/O 映射。VMM 负责为虚拟机分配其所需要的资源,例如进程、内存等。VMM 是虚拟化环境的关键组成部分。

几个开源的虚拟化项目

略。