现代操作系统-虚拟化和云

748 阅读14分钟

虚拟化和云

虚拟化的必要条件

虚拟机管理程序需要在以下三个维度有较好的表现:

1. 安全性:虚拟机管理程序应完全掌控虚拟资源(对运行在虚拟机内部的程序或系统具有完全控制权)。
2. 保真性:程序在虚拟机上执行的行为应与在裸机上相同。
3. 高效性:虚拟机中运行的代码不应受虚拟机管理程序的干涉。

在解释器中逐条地运行指令是一个不错的选择,这被认为是一种安全的方式。有些指令可以直接执行,而有些需要模拟程序做处理等,为了效率,虚拟机试图直接运行大多数的代码。

在具体讨论虚拟机管理程序如何实现安全性之前,先来看看对于指令的划分。每个包含内核态和用户态的指令,都有一个特殊的指令集合,其中的指令在内核态和用户态执行的行为不同。这些指令包括进行I/O操作和修改MMU的指令,它们被称为敏感指令;还有一些指令,在用户态执行会导致陷入内核。它们被称为特权指令。内核态和用户态行为不同的指令称为敏感指令,导致陷入的指令称为特权指令。机器可虚拟化的条件是,敏感指令为特权指令的子集,简单来说就是,在虚拟机里,如果用户态想要做不应该在用户态做的事,那么硬件必须陷入。或者说,虚拟机里,客户程序的敏感指令必须陷入内核执行,而在裸机就不一定了。

Intel和AMD在后期CPU中引入虚拟化支持,在英特尔中被称为VT技术,在AMD中被称为SVM技术。在接下来的叙述中,我们使用VT来作为这两项技术的统称。VT的基本思想是创建可以运行虚拟机的容器。如果运行在容器内的客户操作系统执行了会造成陷入的指令,那么将会触发虚拟机进行模拟(陷入到内核,方法虚拟机就是引发陷入的程序),而这就是x86平台典型的陷入并模拟虚拟机

在没有VT技术的时候,对于不安全的由客户OS产生的指令,会在虚拟机进行二进制翻译,替换掉不属于特权指令的敏感指令,其他的指令可以直接执行。这是二进制翻译技术。

当然了,不是所有的敏感指令都要改写,客户机的用户进程一般都可以直接运行。如果一条敏感指令不是特权指令(言外之意牵扯不到内核态执行),那也不需要。对于属于特权指令的敏感指令,则使用陷入并模拟。第二类虚拟机管理程序必须保证自己可以收到对应的陷入,这需要在内核留一个模块完成此工作。

不同于全虚拟化的另一种技术,被称为半虚拟化。半虚拟化向上暴露一个类似物理机器的软件接口,明确表明自己是虚拟机,客户操作系统请求资源时,就像进行系统调用似的,而不是真的调用硬件接口。

还有一种进程级虚拟化技术,它主要目标是运行另一体系,另一操作系统的程序。

第一类虚拟机管理程序和第二类虚拟机管理程序

从技术讲,第一类虚拟机管理程序就像一个操作系统,因为他是唯一一个运行在最高特权级的程序。它的工作是支持真实硬件的多个虚拟机拷贝。类似普通操作系统运行进程那样。

第二类虚拟机管理程序依赖于实际的宿主操作系统,它像运行在宿主操作系统的一个进程。

第二类虚拟机又称托管型虚拟机管理程序。一般来说,虚拟机找到一个包含客户操作系统的文件,假装引导装载系统。把操作系统安装到虚拟磁盘(通常来说,这是一个宿主操作系统的文件)上,然后开启运行。

高效虚拟化技术

当以为自己运行在内核态的客户OS执行了一条只有真正的内核态CPU才能执行的指令时,发生了什么?通常在不支持VT的机器上,会导致OS崩溃,在支持VT的CPU上,会陷入虚拟机管理程序。如果是客户OS执行了这样的指令,虚拟机管理程序会安排这条指令的顺利执行(检查并执行),如果是客户用户进程,那么虚拟机管理程序会模拟真实的硬件对用户态执行敏感指令时的反应。

在不支持虚拟化的平台进行虚拟化

VMware那群开发,利用二进制翻译和x86确实支持的特权级操作实现了对应的操作。多年来,x86支持四个特权级。用户级程序运行在第3级。而OS运行在第0级,第三级权限最少,因此没法执行特权指令。第0级允许执行任何指令,现在的OS都没使用第1级和第2级。于是虚拟机管理程序解决方案是,自己运行在第0级(内核级),应用程序运行在第3级,客户系统运行在第1级。客户操作系统的特权指令会交给虚拟机管理程序检查,然后代为执行。

虚拟机管理程序要确保客户操作系统的敏感指令不再执行,具体做法是代码改写,一次改写一个基本块。基本块是以转移指令为结尾的一小段顺序指令序列。除了最后一条指令外,内部不包含跳转,调用,陷入,返回,或其他改变控制流的指令。每次执行一个基本块之前,虚拟机管理程序会把基本块里的敏感指令换成调用虚拟机管理程序中处理程序的指令。最后一条也会被替换成调用虚拟机管理程序的程序的指令。翻译过的块可以缓存起来,而且大多数基本块并不包含敏感指令或特权指令。所以翻译开销没有想象中的大。而且,如果可以准确地配置硬件(如VMware做的那样)那么可以直接运行用户程序。

一个基本块执行完毕后,控制流返回虚拟机管理程序,定位下一个块,如果已经翻译过,就直接执行,否则翻译,缓存,执行。最终绝大多数的指令都在缓存里,程序可以以近乎满速运行。中间还会进行各种优化,比方说,某个基本块结尾指令是跳转到另一个基本块,那么可以直接替换成已经翻译好的基本块。用户程序中的敏感指令无需替换,硬件会处理好。

大多数的虚拟机管理程序都在第0级设置一个模块,用来使用特权指令操作硬件。同时,第二类虚拟机,对于宿主操作系统的中断,要及时还原处理器上下文,配置硬件,给宿主操作系统原本的环境,待宿主OS完成后,再进行它自己的操作。

虚拟化的开销

虽然有VT,但是对于陷入并模拟这种做法,会真的陷入内核,这会造成TLB,高速缓存失效等问题,因而这是一个很大的开销。而使用二进制模拟,替换敏感指令,可以避免这个问题,因为替换的结果是简单地调用虚拟机的某个程序。

二进制翻译后,代码可能变快,也可能会变慢,比方说CLI(关闭中断)指令,如果是真的禁用中断,在具有深度流水和乱序执行的CPU上,可能需要数十个时钟周期,但是在虚拟机里,仅仅把为每个虚拟程序提供的CPU数据结构的中断位设置成0即可,让虚拟程序以为自己真的禁用了中断,而这,仅需要几个时钟周期。

如果某个客户操作系统试图修改页表,那么开销就会很大。解决方式可能为把客户操作系统的物理页映射到实际的物理页(所以可以修改映射关系)。

内存虚拟化

虚拟机为客户操作系统创建一个影子页表做内存映射,啥意思呢?就是客户操作系统页表->客户操作系统页框(它所认为的物理地址)->影子页表(虚拟机管理)->实际物理内存地址。可是有一个不好的是,每次客户操作系统修改映射页框时,虚拟机不知道,就很尬。有两个解决方案。

法一是,跟踪客户操作系统的顶级页表指针,因为客户操作系统需要读取存放顶级页表指针的寄存器时,会触发敏感指令,进而被虚拟机捕捉;然后把此客户操作系统的二级页表设为只读,那么每次客户OS试图修改时,都会触发异常,这样会把控制流交给虚拟机,虚拟机进行分析指令流获取其意图,并修改对应的影子页表。

法二是,允许客户机修改页表,不过客户机访问新的页面就会造成缺页异常,然后还是把控制流交给虚拟机。虚拟机探测客户机页表,发现是否对影子页表进行更新。然后重新执行触发异常的指令。对于删除,客户机肯定要调用INVLPG指令(用于删除对应的TLB表项);这是个敏感指令,然后虚拟机截获进行处理。

缺页异常开销很大,不过缺页异常却不同。由客户机访问已被换出的RAM页面的异常称为客户机导致的缺页异常,由虚拟机试图保持影子页表一致的异常称为虚拟机管理程序导致的缺页异常。处理缺页异常会导致虚拟机退出(客户机暂停运行以用来让虚拟机管理程序处理缺页异常),此时虚拟机管理程序重新获得控制流,然后,CPU记录导致虚拟机退出的原因,导致虚拟机退出的客户机指令的地址。接下来,CPU进行上下文切换,保存所有的寄存器。然后CPU载入虚拟机管理程序的处理器状态。然后虚拟机管理程序开始处理异常。处理完毕后再反过来进行程序上下文交换。所以这可能会耗费几万个时钟周期,因此应极力避免虚拟机退出。

不过在半虚拟化的操作系统里,情况略有不同,因为系统知道自己是运行在虚拟机里的,所以它会主动告诉虚拟机管理程序让它更新影子页表。

为了避免影子页表的巨大开销,芯片制造商添加了嵌套页表的硬件支持(AMD叫法)。在Intel里叫扩展页表。这是由硬件进行处理的方式,虽然它很繁琐,同时随着分页层次的增加,访问次数以平方级增长。但是它确实避免了更多的虚拟机退出。

更重要的是,切换虚拟机时,仅需改变对应的嵌套页表映射,就像操作系统为进程切换映射一样。

回收内存技术,用于把某个虚拟机用不到的内存加给另一个虚拟机使用,除了去重化技术,还有一种称为气球的技术,就是在每个虚拟机内部设置一个大小由虚拟机管理程序控制的内存模块,假设每个虚拟机分配的内存大小固定。他会占用客户OS一些内存,然后通过扩大它的大小,让客户OS响应这一变化,重新划分它的页面来缩小其他空间,缩小气球就能让客户OS获得更多可用的内存。通过让客户OS提虚拟机管理程序做决定,这就是气球技术。

I/O虚拟化

I/O MMU,又称I/O内存管理单元,它将具有直接存储器访问能力(可以DMA)的I/O总线连接至主内存。像普通MMU那样,它用页表将设备想要使用的内存地址映射到设备地址。

I/O MMU有很多优势,比方说设备穿透,允许将物理设备直接分配给特定虚拟机。

设备隔离指的是,保证设备可以直接访问到其分配到的虚拟机的内存空间而不影响其他虚拟机的完整性。也就是说I/O MMU可以防止错误的DMA通信,在访问未映射的页面时会触发缺页异常。

I/O MMU还可以进行中断重映射,使中断以正确的中断号抵达正确的虚拟机。比方说一个设备发送了中断号为5的消息,被I/O MMU转换成一个新的中断并抵达目标虚拟机的CPU,中断向量号正是该虚拟机想要的。

设备域旨在解决I/O访问,它的思想是,选取一个专门的虚拟机进行I/O交互,其他虚拟机对于I/O设备的访问的请求转交到此虚拟机进行处理。这就好像真的OS在操作I/O而不是使用虚拟机管理程序进行模拟调用。

单根I/O虚拟化,允许驱动程序与设备间通信绕过虚拟机管理程序而进行。虽然在虚拟机管理程序的控制下,在多个虚拟机间共享设备也是可能的,但是更好的解决措施可能是让设备本身可以虚拟化。这在PCIe里称为单根I/O虚拟化。

有两种访问单根I/O虚拟化的方式,PF和VF,PF是玩真的PCIe功能,允许设备管理员按任意合适的方式进行配置。PF在虚拟机不可访问。VF是轻量级PCIe功能,不提供配置项,是个虚拟机。一个虚拟设备可以提供成百上千个VF供虚拟机使用。

虚拟装置

把软件依赖和虚拟机整合到一块,做到开箱即用

多核CPU上的虚拟机

可以实现更好的对于算力的控制,让一个核心运行多个虚拟机,因此在一个CPU上可以运行很多个虚拟机,这样就可以实现更好的多机系统。而且通过内存共享,可以让他们更好的协作运行。

集中的计算资源管理,可以实现对于用户的计算服务,改变了常规的服务器模式,让企业需要的服务由云厂商统一提供。

关于虚拟机迁移,有一个称为热迁移的方式,对当前虚拟机内存页面进行复制,并在复制后更新复制过程中发生更改的页面。

还可以对虚拟机进行快照,以防崩溃进行回滚。

案例研究:VMware

下一篇-多处理机系统