本节摘要:虚拟化是把一台物理机合法隔成多台虚拟机的技术,而它的成败系于指令集的一念之间:敏感指令能否可靠地被"抓住"。本节讲虚拟化的经典难题、纯软件时代的艰辛,以及 VT-x、ARM EL2、RISC-V H 扩展三家的硬件化答案。读完你应当能解释"虚拟机为什么曾经不可能、后来又为什么便宜了"。
虚拟化的门禁悖论是这样的:虚拟机监控器(VMM, aka Hypervisor)承诺每台虚拟机"独占一台真机",包括独占那些特权指令——客户内核要开中断、要切页表,监控器既不能真让它改物理状态,又不能不让它执行。经典解法是陷入与模拟:把敏感指令设成陷阱,客户一执行就掉进监控器,由监控器判断意图、模拟效果、再放行。理论标准(Popek 与 Goldberg 的虚拟化判据)据此提出:所有敏感指令都必须是特权指令——即必须在低特权级触发陷阱。
麻烦在 x86 的老地层(3.2 节)上:早年它的若干指令在用户态执行静默失败而不陷阱——最著名的如改标志寄存器特权位的弹栈指令,低特权下直接把该位悄悄丢弃、程序浑然不觉。监控器根本没机会出场。这类"敏感却非特权"的指令不止一条,按严格判据,x86 一度是"不可高效虚拟化"的架构。世纪之交的软件方案各显神通:扫描改写客户代码(把敏感指令替换成陷入调用)、或干脆全量二进制翻译(逐条把客户指令翻成宿主指令)——能用,但开销百分之几到几十不等,虚拟化成了奢侈品。
转机是给特权模型再加一层。Intel 的 VT-x 引入根模式与非根模式:监控器住根模式(底下还有它自己的 ring 0),客户整机(含客户内核)住非根模式。非根模式里,预设的敏感事件——执行敏感指令、访问关键寄存器、发生缺页——不再静默,而是触发虚拟机退出(VM exit):硬件自动保存客户现场,控制权交回监控器,监控器模拟完再以虚拟机进入(VM entry)放客户继续。客户与监控器的切换由专用指令驱动:vmlaunch(0F 01 C2,首次进入客户)、vmresume(0F 01 C3,再次进入)、客户内主动呼叫监控器的 vmcall(0F 01 C1);每台虚拟机的控制信息存放在一块专用结构(VMCS)里,由 vmread 与 vmwrite 配置。这一套指令是 1.3 节"重扩展要惊动全栈"的又一次应验——新异常级别、新状态块、内核跟进,换来的是"敏感事件必然可控"的干净语义。
ARM 把座席直接排进异常级别序列:EL2 专职监控器(客户内核在 EL1、应用在 EL0),跨级求助用 hvc(编码 0xD4000002 起)与安全世界的 smc(0xD4000003);RISC-V 的 H 扩展照此办理,新增 HS 模式与配套的 CSR。三家的共同点:把"抓住敏感指令"从软件侦探工作变成硬件电路判定。
地址翻译的虚拟化是另一半战场。6.1 节的页表把虚拟地址翻成"物理地址",可虚拟机里的"物理地址"还是假的(客户物理地址),监控器还得再翻一层到真物理地址。软件方案影子页表:监控器维护一张直接从客户虚拟地址到真物理地址的合成页表喂给硬件,客户自己的页表形同虚设——代价是影子表与客户页表的一致性维护,客户每改一次页表,监控器都要跟着改影子,开销随缺页风暴暴涨。硬件方案两阶段翻译:MMU 原生支持两级页表(客户表翻一阶段、监控器表翻二阶段),硬件自动完成,客户改自己的页表监控器完全无感。
两阶段翻译的代价同样真实:翻译链变长,TLB 要同时缓存两级结果,未命中遍历翻倍——所以两阶段页表的 TLB 压力管理(大页、嵌套 TLB)是云主机调优的固定科目。影子页表并未绝迹,它退守到两阶段翻译未覆盖的角落(部分嵌套虚拟化与老硬件)。
把退出成本落到具体动作上。设想客户内核配置一个定时器:它写自己的本地中断控制器寄存器,而这正是监控器预设的敏感事件。旅程开始:写指令一发出,硬件发现目标落在拦截位图里,立即中止执行并触发虚拟机退出;处理器把客户的全套架构状态(通用寄存器、控制寄存器、程序计数)存入 VMCS 的客户状态区,切到根模式,从监控器的入口点恢复。监控器读出退出原因,识别出"客户在写定时器",用软件替它完成等效动作——真的去配物理定时器、登记一个到期事件——随后 vmresume 把现场装回去,客户从下一条指令继续,对这次"被打断并代劳"全程无感。整个往返在现代硬件上以微秒计,其中保存与恢复现场的固定开销占去大头,监控器的模拟逻辑本身反而是小头。这笔账解释了两件事:为什么半虚拟化要把高频敏感操作换成约定通道(一次退出都不付);为什么监控器工程师的性能档案里"退出原因分布图"是最重要的一张——所谓调优,就是把高频退出原因一个个搬出这条昂贵通道。
第一个坑:把"硬件虚拟化"当成"零开销"。VM 退出是系统调用之上的更大手笔:客户全套现场保存加模式切换加模拟逻辑,一次退出的代价以微秒计——高频退出的负载(网络包处理、时钟密集型客户)会被退出风暴拖垮,云调优的半壁江山就是压退出次数(半虚拟化驱动让客户知趣地走约定通道,而不是撞敏感指令的墙)。第二个坑:忽视嵌套虚拟化的层级放大。虚拟机里再跑虚拟机,两阶段翻译叠成多级,退出的保存链也层层加码——嵌套场景的性能衰减是乘法不是加法。第三个坑:安全边界的错觉。虚拟化是隔离手段不是安全边界本身:7.4 节的侧信道攻击可以穿透虚拟机边界(共享缓存就是共享泄漏面),云上租户隔离的完整论证需要把本章的门禁与下一章的锁放在一起读。