Lecture 27: 虚拟机(Virtual Machines)
Lecture 27: 虚拟机(Virtual Machines)
概述
进程抽象只是底层机器的一个子集(部分指令、虚拟内存、系统调用);如果让”进程”拥有完整的机器(全部指令与寄存器、物理内存视图、MMU、I/O 设备、陷阱与中断),它就是一台虚拟机(VM)。本讲讨论虚拟机监视器(hypervisor)的实现:模拟 vs 直接执行 + 陷阱模拟、虚拟 I/O 与虚拟内存(影子页表/嵌套页表),以及虚拟机如何成就现代数据中心与云计算。
核心概念与系统机制图解
进程 vs 虚拟机:抽象层级的差异
进程抽象(OS 提供): 虚拟机抽象(hypervisor 提供):
● 内存: 虚拟内存页的线性数组 ● CPU: 全部指令+寄存器(含特权指令)
● CPU: 所有非特权指令+寄存器 ● 内存: 物理内存页 + MMU(页表等)
● 系统调用: 文件/进程操作等 ● I/O 设备: 定时器/磁盘/网卡/显示
● 是底层机器的子集 ● 陷阱与中断: 系统调用只是普通陷阱
● 是一台"完整私有机器"
- 运行在 VM 里的 OS 叫客户操作系统(guest OS);管理 VM 的 OS 叫 hypervisor(虚拟机监视器, VMM);一台物理机可同时运行多个不同 guest OS 的 VM。
实现方式 1:完全模拟(Simulation)
- 用程序模拟 CPU 指令、MMU 与物理内存(一个大数组)、I/O 设备(磁盘用镜像文件)。
- 太慢:CPU/内存慢 100 倍,I/O 慢 2 倍。
实现方式 2:直接执行 + 陷阱模拟(Direct Execution)——主流
把 guest OS 放在"用户模式"运行:
● 绝大多数指令以全速直接执行
● 特权指令(如 CLI/STI/POPF/HALT)执行时产生非法指令陷阱 → hypervisor 模拟
● 特权指令在内核代码中相对罕见 → 模拟开销占比小
CLI(关中断)的模拟示例:
1. guest OS 在用户模式执行 CLI → 非法指令陷阱
2. hypervisor 的陷阱处理器运行(IDT 指向 hypervisor)
3. hypervisor 检查到是 CLI → 模拟: 把"本 VM 的中断屏蔽"标记置位
4. 返回陷阱: CPU 回到用户模式, guest OS 从 CLI 下一条继续, 中断已被屏蔽
guest 内的系统调用(应用 syscall → guest OS sysret):
应用执行 syscall → 陷阱进 hypervisor(机器IDT) → hypervisor 模拟 syscall
→ 设置 vCPU 为内核模式 → 返回用户模式 → guest OS 执行系统调用
→ guest OS 执行 sysret → 再次陷阱进 hypervisor → hypervisor 模拟 sysret
→ vCPU 回用户模式 → 应用继续
虚拟 I/O 设备
- guest OS 读写”虚拟设备寄存器”时,hypervisor 让这些访问触发陷阱,由陷阱处理器模拟设备功能;完成后在虚拟 CPU 上模拟一个中断。
- 减少陷阱数:为 guest 写新的设备驱动,用 hypervisor 调用(系统调用)代替设备寄存器访问 → 半虚拟化(paravirtualization)。
虚拟化虚拟内存:影子页表与嵌套页表
guest 虚拟AS → guest "物理"AS → host "机器"AS
影子页表(Shadow Page Maps):
hypervisor 维护"guest虚拟→机器物理"的合成页表, 装入真实MMU
guest 每次改页表都要 hypervisor 拦截更新影子表 → 开销高
x86-64 硬件扩展(Intel EPT / AMD NPT):
增加一层页表: VM内页表(VPN→PPN) + hypervisor页表(PPN→MPN)
硬件自动完成两级翻译, 无需影子表 → 大幅降低开销
虚拟机的价值与历史
封装性: VM 封装全部执行状态 → 可复制、保存、迁移
数据中心整合: 一台机器跑多个"单应用 VM"(隔离+利用率) → 云计算的基石
历史: 1960s IBM 发明(一台机器多用户); 80-90年代沉寂(人人有PC);
1990s 中期因 Windows 垄断地位而复兴(兼容性测试); 2000s 数据中心整合 → 云
代码示例与系统调用解说
示例:用 Linux KVM/QEMU 体验虚拟机(命令行形态)
# 1. 创建磁盘镜像(模拟一块"物理磁盘")
qemu-img create -f qcow2 myvm.qcow2 8G
# 2. 启动一台虚拟机(客户 OS 的"裸机")
qemu-system-x86_64 \
-enable-kvm \ # 使用硬件虚拟化扩展(嵌套页表)
-m 2048 \ # 给 VM 2GB "物理内存"
-drive file=myvm.qcow2,format=qcow2 \
-cdrom ubuntu.iso # 安装 guest OS
【代码做了什么?】 创建磁盘镜像并启动一个完整 x86 机器(CPU、内存、磁盘、BIOS)运行客户操作系统。
【系统机制透视】
-enable-kvm开启 Intel VT-x/AMD-V:CPU 硬件直接支持”guest 模式”,特权指令自动陷入 hypervisor(VMM),配合 EPT 嵌套页表实现两级地址翻译——这正是 Lecture 27 讲的”直接执行 + 硬件辅助”。- 磁盘镜像是”用文件模拟物理磁盘”:文件系统(qcow2 格式)在宿主机上存储 VM 的块设备——最外层的文件系统”虚拟化”了里层的文件系统。
关键要点
- 进程抽象是机器的子集;VM 让”进程”拥有完整机器(CPU/内存/MMU/I/O/陷阱)。
- hypervisor 用”直接执行 + 特权指令陷阱模拟”达到接近原生的性能。
- 虚拟 I/O 与半虚拟化减少陷阱开销;影子页表被硬件嵌套页表(EPT/NPT)取代。
- VM 的封装性(复制/迁移/隔离)催生数据中心整合与云计算。
- 虚拟机 = 操作系统级虚拟化的”终极形态”,与线程(CPU 虚拟化)、文件(存储虚拟化)一脉相承。
常见陷阱与注意事项
- 把模拟与虚拟化混为一谈:完全模拟慢 100 倍;直接执行才是主流。
- 忘记特权指令陷阱:没有陷阱机制,guest 能执行特权指令破坏宿主机。
- 影子页表开销:guest 频繁改页表时维护开销大(硬件嵌套页表解决)。
- 忽略 I/O 虚拟化开销:设备模拟是 VM 性能瓶颈之一(半虚拟化/直通缓解)。
思考题
- 问题:为什么”直接执行”能让 guest OS 以近全速运行?
- 答案:guest 的大多数指令(算术、访存、控制流)在用户模式下直接执行,无需模拟;只有特权指令(相对稀少)才陷阱进 hypervisor 模拟——模拟占比小,总体接近原生速度。
- 问题:影子页表解决了什么问题?为什么后来被嵌套页表取代?
- 答案:影子页表把”guest 虚拟→机器物理”合成为真实 MMU 使用的表,解决了”guest 页表不能直接用”的问题;但 guest 每次修改页表都要 hypervisor 拦截并重建影子表,开销高。硬件嵌套页表(EPT/NPT)在硬件中完成两级翻译,免去影子表维护。
