外观
第 3 课 虚拟化原理:虚拟机是怎么跑起来的
约 5837 字大约 19 分钟
虚拟化CPU 虚拟化内存虚拟化I/O 虚拟化
2026-08-17
项目目标
前两课你已经能用 virt-manager 创建并安装一台 Ubuntu 服务器。本课回答一个更底层的问题:虚拟机里的系统,凭什么能安全地共享宿主机的 CPU、内存和磁盘?学完本课,你能解释 CPU 虚拟化的保护环与 VM Exit/Entry、内存虚拟化的两层地址翻译、I/O 虚拟化的几种实现路径,并说清它们各自的代价与适用场景。本课会用第 2 课创建的 svr01 做三次只读观察,把原理落到命令证据上。
先花两分钟确认你的起点。下面的自测结果会帮课程助教判断该从哪里开始讲。
章节测评
3 题开课自测:虚拟化原理基础
用 3 道题确认你对虚拟机内部机制有没有初步印象。答错不扣分。
3.1 从“能创建”到“懂原理”
前两课你跟着向导创建了 svr01:分配了 2GB 内存、25GB 磁盘、选了 NAT 网络,然后它就能跑起来。这背后藏着一个看似矛盾的问题:
核心矛盾
svr01 里跑着一个完整的 Ubuntu 内核,它以为自己独占了一台电脑的 CPU、内存和磁盘。但物理机上只有一个 CPU、一块内存、一块磁盘,而且还要同时喂给宿主机和其他虚拟机。虚拟化是怎么做到“让每个系统都以为自己独占,实际却共享同一份硬件”的?
这个问题的答案分成三块,正好对应计算机的三大件:
| 硬件 | 虚拟化的核心问题 | 关键技术 |
|---|---|---|
| CPU | 两个内核都要最高特权,怎么共存 | 保护环、VM Exit/Entry、VT-x/AMD-V |
| 内存 | 每个系统都从地址 0 开始管理内存,怎么隔离 | 两层地址翻译、EPT |
| I/O 设备 | 多台虚拟机怎么共享一块网卡/磁盘 | 软件模拟、VirtIO、直通、SR-IOV |
下面三节逐一展开。原理讲的是业界通用机制,KVM、VMware、Hyper-V 等产品都建立在这些机制之上。
实训准备:先确认对象与执行位置
本课的三组观察都以 Ubuntu 宿主机终端为入口,并显式连接系统级 libvirt:qemu:///system。先在同一个宿主终端设置本课参数;这些只是当前 Shell 会话中的变量,关闭终端后需要重新设置。
使用课程默认名称
VM_NAME='svr01'
GUEST_USER='cloudstudent'使用自定义名称
把下面两个示例值换成创建虚拟机时实际使用的名称和来宾账号:
VM_NAME='ubuntu-server-01'
GUEST_USER='ubuntu-admin'列出系统连接中的全部虚拟机,再核对参数指向的虚拟机是否正在运行:
virsh --connect qemu:///system list --all
virsh --connect qemu:///system domstate "$VM_NAME"
virsh --connect qemu:///system domifaddr "$VM_NAME" --source lease从 domifaddr 的输出中找到来宾的 IPv4 地址,将下面的示例地址替换为实际值:
GUEST_IP='192.168.122.100'domstate 应显示 running。如果 domifaddr 暂时没有地址,先回到第 2 课确认来宾网络和 DHCP 租约,不要继续套用示例地址。
I/O 观察会使用 qemu-img。在 Ubuntu 中该命令由 qemu-utils 软件包提供(版本差异以实际环境为准),先检查命令是否存在:
command -v qemu-img如果没有输出,在进入三组只读观察前完成一次软件准备:
sudo apt update
sudo apt install -y qemu-utils宿主与来宾不要混用
本课所有 virsh、cat /sys/module/...、ls、du 和 qemu-img 命令都在宿主机执行。来宾侧的 lscpu、free 会写在 SSH 命令的引号中,由 SSH 在来宾内执行,命令结束后仍停留在宿主终端。
3.2 CPU 虚拟化:让来宾内核安全地“以为自己是老大”
保护环:CPU 的等级制度
CPU 把运行权限分成几个等级,称为保护环(Protection Rings)。x86 架构有 Ring 0 到 Ring 3 四级:
| 等级 | 名称 | 谁在用 | 能做什么 |
|---|---|---|---|
| Ring 0 | 内核态 | 操作系统内核 | 直接访问硬件、管理内存、执行所有指令 |
| Ring 1/2 | (很少用) | 部分驱动 | 历史遗留,现代系统基本不用 |
| Ring 3 | 用户态 | 普通应用程序 | 只能做日常计算,碰硬件要请内核代办 |
城堡与通行证
把 CPU 想成一座城堡:Ring 0 是国王寝宫,只有最高通行证能进;Ring 3 是平民区。普通程序(浏览器、游戏)在 Ring 3 运行,想保存文件、访问网络,必须通过系统调用请求内核帮忙——CPU 会切换到 Ring 0 执行内核代码,完成后再降回 Ring 3。
特权冲突:两个内核都要住进 Ring 0
问题来了:宿主机内核要运行在 Ring 0,来宾内核(svr01 里的 Ubuntu)也需要 Ring 0 才能管理自己的硬件。但物理 CPU 只有一个 Ring 0——特权冲突。
虚拟化的核心,就是让来宾内核能在自己的 Ring 0 中运行,同时仍受虚拟化层控制。CPU 根据执行控制拦截需要由宿主处理的操作,其他允许直接执行的指令不必退出。
先分清两个分类维度
“全虚拟化、半虚拟化、二进制转译、硬件辅助”经常一起出现,但它们回答的不是同一个问题:
| 分类维度 | 路线 | 回答的问题 | KVM/QEMU 中的例子 |
|---|---|---|---|
| 来宾是否需要配合 | 全虚拟化 | 来宾能否把虚拟硬件当成普通硬件,无需修改内核 | Ubuntu 不修改内核也能在 KVM 上启动 |
| 来宾是否需要配合 | 半虚拟化 | 来宾是否使用专为虚拟化设计的接口主动协作 | VirtIO 网卡、磁盘与气球驱动 |
| CPU 指令怎样执行 | 二进制转译或软件模拟 | 没有硬件虚拟化能力时,怎样翻译或模拟来宾指令 | QEMU 的 TCG 加速器 |
| CPU 指令怎样执行 | 硬件辅助虚拟化 | 怎样借助 CPU 的虚拟化扩展直接运行大部分来宾指令 | KVM 使用 VT-x 或 AMD-V |
本课程用的是哪条路
KVM 走的是硬件辅助虚拟化主线:CPU 提供 VT-x(Intel)或 AMD-V(AMD)扩展,大部分来宾指令可以直接在真实处理器上运行,需要拦截的操作再退出给 KVM 处理。这正是第一章检查 vmx/svm 标志的原因。没有硬件辅助时,type='kvm' 的来宾不能启动;如果确实要做纯软件模拟,需要改用 QEMU 的 TCG 加速器,而不是由 KVM 自动降级。
硬件辅助:宿主模式与来宾模式
以 Intel VT-x 为例,CPU 增加了与 Ring 0–3 正交的两种 VMX 运行模式:
- VMX Root 模式:宿主侧使用;KVM 内核代码运行在 Root 模式的 Ring 0。
- VMX Non-Root 模式:给虚拟机用,里面也有一整套 Ring 0–3,来宾内核可以在 Non-Root 的 Ring 0 上原生运行。
QEMU 仍然是宿主机的用户态进程,并不运行在 Root 模式的 Ring 0。根据虚拟机执行控制配置,当来宾执行需要拦截的操作时,CPU 触发 VM Exit 并先进入 KVM 内核;KVM 能处理的退出直接在内核完成,需要用户态设备模型的退出才返回 QEMU。处理完成后,再通过 VM Entry 返回来宾。
Intel 用 VMCS 保存虚拟机执行控制和状态;AMD-V/SVM 使用对应的宿主/来宾模式与 VMCB。下面沿用“VM Exit/Entry”作为通用教学称呼,但要记住具体结构和指令名称因厂商而异。
下面的动画演示一次 VM Exit/Entry 的完整旅程:
一次 VM Exit / VM Entry 的完整旅程
观察 VM Exit 先进入 KVM 内核、何时才返回 QEMU 用户态,以及怎样重新进入来宾。
1 / 6
来宾
svr01 的来宾内核执行受执行控制约束的操作
CPU 硬件
执行进入、退出与状态切换
KVM 内核
处理硬件虚拟化与常见退出
QEMU 用户态
按需处理设备模型
观察要点:
- 只有被执行控制设置为需要拦截的操作,才会触发 VM Exit;不是每条特权指令都必然退出。
- VM Exit 先进入 KVM 内核;只有需要用户态设备模型时,KVM 才把控制返回 QEMU。
- 从来宾到内核、必要时再到用户态的切换都有成本,所以虚拟化设计会尽量减少不必要的退出。
动手观察:CPU 虚拟化
验证“vCPU 是调度出来的虚拟执行上下文,不是独占的物理核”。先在宿主机查看当前与最大 vCPU 数量,再从 XML 中核对 <vcpu> 定义:
virsh --connect qemu:///system vcpucount "$VM_NAME"
virsh --connect qemu:///system dumpxml "$VM_NAME" | grep -E '^[[:space:]]*<vcpu([[:space:]>])'然后从宿主终端发起一次 SSH 远程命令,让 lscpu 在来宾内运行:
ssh "${GUEST_USER}@${GUEST_IP}" 'lscpu | grep -E "^CPU\(s\)|Model name"'预期
vcpucount 会区分配置值、运行值、当前值与最大值;XML 的 <vcpu> 行记录持久配置。来宾 lscpu 显示的在线 CPU 数应与当前运行配置对应,不要求每个人都固定为 2。即使数量一致,这些 vCPU 仍由宿主调度器安排到物理 CPU 上运行,不代表来宾独占同等数量的物理核。
3.3 内存虚拟化:两层地址翻译
回顾:操作系统怎么管理内存
程序使用的地址是虚拟地址,真实内存条上的地址是物理地址。CPU 里的 MMU(内存管理单元) 负责翻译:它查询操作系统维护的页表,把虚拟地址映射到物理地址。每个进程都有一份独立的页表,所以进程 A 访问不到进程 B 的内存。
双重翻译:虚拟化带来的难题
虚拟化环境里,地址翻译变成了两层:
- 第一层(来宾内部):来宾程序用虚拟地址(GVA),来宾内核把它翻译成“来宾以为的物理地址”(GPA)。
- 第二层(Hypervisor):GPA 其实是假的,QEMU/KVM 必须再把它翻译成真实的机器物理地址(MPA)。
影子页表:早期的软件方案
早期虚拟化常用影子页表:KVM 根据来宾页表维护一套可由硬件直接使用的 GVA→MPA 映射。映射已经存在时,普通内存访问可以直接命中影子页表;来宾修改页表,或者访问缺失、受保护的映射时,KVM 才可能通过 VM Exit 捕获事件并同步影子页表。维护两套页表及其一致性,是这条路线的主要开销。
EPT/NPT:让硬件支持二维页表遍历
现代 CPU 提供 EPT(Intel)和 RVI/NPT(AMD)硬件特性,让 MMU 原生支持两级翻译:
- 来宾内核自由维护自己的页表(GVA→GPA)。
- KVM 维护第二级页表(GPA→MPA),把基址告诉 CPU。
- 普通访问在两级映射都有效时,由硬件完成 GVA→GPA→MPA 的二维页表遍历,不需要为了每次地址翻译都退出。
- 如果第二级映射缺失、权限不允许,或地址对应 MMIO,仍可能发生 EPT violation/NPT fault 并触发 VM Exit。
为什么 EPT 是性能关键
影子页表需要软件持续维护合成映射;EPT/NPT 把正常访问的两级遍历交给硬件,显著减少同步类 VM Exit。页表项命中 TLB 时开销很低;TLB 未命中时仍要进行多级页表遍历,并不是固定“一个周期”。
下面的动画把影子页表与 EPT/NPT 画成两条替代路径,并分别展示正常命中和异常退出:
一次内存访问,CPU 翻译了几层?
对比影子页表与 EPT/NPT 两条替代路径,分别观察正常命中和异常退出。
1 / 6
来宾视角
程序访问 GVA:0x0040_0000Hypervisor 视角
选择影子页表或 EPT/NPT 机制CPU 动作
两条路径是替代方案,不会在一次访问中先后执行本步无需 VM Exit
观察要点:
- 影子页表让硬件直接使用 KVM 合成的 GVA→MPA 映射,正常命中时不必每次退出。
- 来宾页表变化或影子映射缺失时,KVM 需要介入维护一致性。
- EPT/NPT 让硬件执行二维页表遍历;正常映射无需退出,但 EPT violation/NPT fault 仍需要处理。
高级内存管理
| 技术 | 作用 | 通俗理解 |
|---|---|---|
| 内存超售 | 分配给所有虚拟机的内存总和可以超过物理内存 | 不是所有虚拟机同时用满,赌一个“错峰” |
| 透明页共享 | 内容相同的页面只存一份,多台虚拟机共享 | 多台 Windows 都加载同一个 dll,只留一份 |
| 内存气球 | 由宿主请求来宾归还一部分可用内存 | 气球驱动在来宾中占用页面,宿主回收对应页;来宾压力升高时可能丢弃缓存或使用交换空间 |
动手观察:内存虚拟化
先在宿主机确认 CPU 厂商:
grep -m1 '^vendor_id' /proc/cpuinfo输出包含 GenuineIntel 时查看 EPT,包含 AuthenticAMD 时查看 NPT,只执行与你的 CPU 对应的一项:
Intel(EPT)
cat /sys/module/kvm_intel/parameters/eptAMD(NPT)
cat /sys/module/kvm_amd/parameters/npt预期
输出 Y 表示 EPT/NPT 已启用,对应本节的“硬件二维页表遍历”;输出 N 表示被关闭,页表遍历退回软件路径,性能明显下降。
再在宿主机查看内存上限、内存气球设备与运行时统计:
virsh --connect qemu:///system dominfo "$VM_NAME"
virsh --connect qemu:///system dumpxml "$VM_NAME" | grep -E '<memballoon([[:space:]>])'
virsh --connect qemu:///system dommemstat "$VM_NAME"预期
dominfo 的 Max memory 是配置允许的内存上限;memballoon 表示来宾配置了内存气球设备。dommemstat 中的 actual 是当前气球值,rss 是宿主上该虚拟机进程的驻留集大小,两者都以 KiB 为单位。rss 包含的是宿主进程视角的数据,可能小于或大于 actual,不能把它直接当作来宾“已用内存”。
最后从宿主终端发起 SSH 远程命令,在来宾内查看来宾操作系统自己的内存统计:
ssh "${GUEST_USER}@${GUEST_IP}" 'free -h'预期
free -h 是来宾操作系统视角,total 通常接近当前提供给来宾的内存,但会受固件、内核保留区域和气球状态影响。比较 Max memory、actual、rss 与 free -h 时,重点说明每个数来自哪一层,不要求它们相等,也不能仅凭大小关系推断来宾负载。
3.4 I/O 虚拟化:让虚拟机又快又稳地存取
多台虚拟机共享一块物理网卡、一块物理磁盘,Hypervisor 得像交通警察一样保证:每个数据包都投递给正确的虚拟机,互不混淆,还不能太慢。
四种实现路径
| 路径 | 做法 | 性能 | 共享 | 典型场景 |
|---|---|---|---|---|
| 软件模拟 | 模拟一块经典网卡/磁盘,来宾用自带驱动 | 低 | 高 | 兼容性优先 |
| 半虚拟化(VirtIO) | 来宾装前端驱动,与 QEMU、vhost 或其他宿主后端通过 virtqueue 协作 | 高 | 高 | 现代虚拟化默认 |
| 设备直通 | 整块物理设备直接给一台虚拟机 | 极高 | 无(1:1) | 追求极致性能 |
| SR-IOV | 物理设备硬件上切成多个虚拟功能,分别给不同虚拟机 | 高 | 高 | 数据中心高性能场景 |
VirtIO:半虚拟化的代表
软件模拟慢,是因为每笔 I/O 都要模拟传统设备行为。VirtIO 换了个思路:来宾里装一个轻量前端驱动,宿主侧由 QEMU、vhost 内核模块或其他后端实现虚拟设备;双方通过共享内存中的 virtqueue 交换描述符和数据。来宾要发数据,前端驱动把请求放进队列并通知后端,减少传统设备模拟开销。
KVM 里的 VirtIO
你在创建 svr01 时,磁盘控制器和网卡都选了 VirtIO 半虚拟化设备;QEMU 在宿主机侧提供后端驱动,来宾里由 Linux 内核自带 virtio 前端驱动,I/O 性能明显好于纯软件模拟。第一章 1.6 节讲过的“半虚拟化”就在这里落地。
设备直通与 SR-IOV
- 设备直通:把一张物理网卡整个交给一台虚拟机,虚拟机装原生驱动,性能接近物理机。代价是设备不能再被其他来宾共享,而且通常会限制带内存快照、挂起和热迁移;少数支持状态迁移的设备与软件栈可以例外。需要 IOMMU(Intel VT-d / AMD-Vi)限制设备可访问的内存范围。
- SR-IOV:物理设备在硬件层面把自己切成多个“虚拟功能”(VF),每个 VF 像一个小网卡,分别直通给不同虚拟机。兼顾高性能和共享。
动手观察:I/O 虚拟化
在宿主机分别观察虚拟机类型、网卡清单、磁盘清单,以及 XML 中的磁盘和网卡定义:
virsh --connect qemu:///system dumpxml "$VM_NAME" | grep -m1 '<domain type='
virsh --connect qemu:///system domiflist "$VM_NAME"
virsh --connect qemu:///system domblklist "$VM_NAME" --details
virsh --connect qemu:///system dumpxml "$VM_NAME" | sed -n -e '/<disk /,/<\/disk>/p' -e '/<interface /,/<\/interface>/p'如果第二课沿用了推荐配置,通常会看到 domain type='kvm',domiflist 的 Model 列为 virtio,磁盘 XML 的 <target> 也会显示 bus='virtio'。这些结果只能证明来宾暴露了哪类虚拟设备,不能单凭 XML 判断实际吞吐量或延迟。
从 domblklist 的 Source 列找到要观察的磁盘文件,把实际路径写入 DISK_PATH。下面只展示变量写法,文件名不要求与虚拟机名称相同:
DISK_PATH='/var/lib/libvirt/images/svr01.qcow2'
sudo ls -lh "$DISK_PATH"
sudo du -h "$DISK_PATH"
sudo qemu-img info --force-share "$DISK_PATH"预期
ls -lh 显示文件的逻辑长度,du -h 估算宿主文件系统已经分配的空间;qemu-img info 会自动识别镜像格式,并分别给出 virtual size 与 disk size。动态分配的 qcow2 镜像通常表现为虚拟容量大于已分配空间,但三个工具的统计口径不同,数值不必完全一致。
正在运行的镜像只做观察
qemu-img info --force-share 以共享只读方式查询正在被 QEMU 使用的镜像,不会修改磁盘;如果来宾同时写入,镜像元数据也可能在变化,因此输出只是观察时刻的近似结果。不要把 info 换成 check -r、resize、convert 等会检查修复或改写镜像的子命令。
现在根据“兼容性、性能、共享能力”三个条件,为下面场景选择路径:
| 场景 | 第一优先级 | 你的选择 |
|---|---|---|
| 老旧来宾没有 VirtIO 驱动,只要求先启动 | 兼容性 | |
| 通用 Linux 服务器,需要较好的磁盘与网络性能 | 性能与易管理 | |
| 一台计算来宾独占整块 GPU | 极致性能 | |
| 多台来宾共享一张支持虚拟功能的高速网卡 | 低延迟与共享 |
阶段验收
你能从 XML 中指出一个虚拟设备模型,并能为四个场景分别说明“为什么选、牺牲了什么”,就完成了本节验证。
本课实训提交与验收
三个观察任务分别出现在 3.2、3.3、3.4 对应原理小节之后。完成全部观察后,把三组命令输出分别截图提交:
- CPU 证据截图:
vcpucount、XML 的<vcpu>行和来宾lscpu输出; - 内存证据截图:EPT/NPT 参数、
dommemstat和来宾free -h输出; - I/O 证据截图:设备清单或 XML,以及
ls -lh、du -h、qemu-img info的大小对照。
结论不需要单独写文本:验收时能指着截图说清 vCPU 调度、内存分层和 VirtIO/qcow2 三个结论即可(见下方验收标准)。
验收标准:
- 能指出观察目标虚拟机(
VM_NAME变量指向的来宾,默认svr01)的当前 vCPU 数与 EPT/NPT 状态,并说明“分配 vCPU ≠ 独占物理核”; - 能分别说明
Max memory、actual、rss和free -h属于哪一层观察口径; - 能从设备清单或 XML 辨认 VirtIO,并用逻辑长度、宿主分配空间和虚拟容量解释 qcow2 动态分配;
- 除课前补装缺失的
qemu-utils外,三组观察全程只读,未修改虚拟机配置或磁盘内容。
3.5 GPU 虚拟化:怎样分配图形与计算能力
GPU 虚拟化:给虚拟机分算力
GPU 是高度并行的专用处理器,和 CPU 的虚拟化思路不同。三种主要路线:
| 路线 | 做法 | 性能 | 共享 |
|---|---|---|---|
| API 转发 | 把来宾图形 API 或渲染命令转给宿主 GPU | 取决于实现与工作负载 | 高 |
| GPU 直通 | 整块 GPU 给一台虚拟机,装原生驱动 | 极高 | 无 |
| vGPU | 通过时间片、媒介设备、SR-IOV 或硬件分区等机制提供虚拟 GPU | 高 | 高 |
vGPU 是共享数据中心 GPU 的常见路线之一,但“vGPU”不是单一实现:有的按时间片共享,有的使用媒介设备或 SR-IOV,有的提供硬件隔离分区。能否用于 VDI、AI 训练或云游戏,取决于具体 GPU、驱动、授权和工作负载。
3.6 虚拟化安全:隔离还会从哪里失效
虚拟化安全:隔离不是绝对保险
虚拟化引入新的攻击面,核心威胁包括:
| 攻击 | 目标 | 常见手段或例子 | 代表案例 |
|---|---|---|---|
| 虚拟机逃逸 | 从虚拟机攻破宿主机/Hypervisor | 利用虚拟硬件驱动漏洞 | VENOM(虚拟软驱漏洞) |
| 侧信道泄露 | 跨隔离边界推断其他上下文中的数据 | 利用缓存、推测执行等微架构状态 | Spectre、Foreshadow |
| 横向移动 | 从已失陷来宾继续攻击其他系统 | 窃取凭据、利用错误网络隔离或服务漏洞 | 取决于具体网络与身份系统 |
| 管理平面攻击 | 控制整个虚拟化平台 | 利用管理软件漏洞、弱口令 | vCenter RCE |
防御思路:加固 Hypervisor(最小化攻击面、及时打补丁、可信启动)、强化隔离(网络分段、微分段)、保护管理平面(管理网络隔离、RBAC、MFA、审计日志)。虚拟化隔离能缩小影响范围,但安全仍需系统化建设。
3.7 本章小测
先独立作答,再核对解析。下面的问题组会直接给出反馈。
章节测评
5 题本章小测:虚拟化原理
5 道题,检查你是否理解 CPU、内存与 I/O 虚拟化的核心机制。
本章小结
- CPU 虚拟化解决特权冲突:保护环把权限分级,硬件辅助虚拟化(VT-x/AMD-V)让来宾内核在受限模式原生运行,敏感指令通过 VM Exit/Entry 交给 Hypervisor。
- 内存虚拟化解决双重翻译:影子页表靠软件同步、开销大;EPT/NPT 让硬件一次完成 GVA→GPA→MPA,是现代性能关键。
- I/O 虚拟化在兼容、性能、共享之间权衡:软件模拟兼容好性能差,VirtIO 半虚拟化是现代默认,设备直通追求极致但无法共享,SR-IOV 兼顾高性能与共享。
- 三种虚拟化(CPU/内存/I/O)是业界通用机制,KVM、VMware、Hyper-V 都建立其上。
下一课将回到实训,用 virt-manager 创建一台带图形桌面的 Ubuntu Desktop 来宾 desk01,体验虚拟显卡、显示链路和增强功能带来的桌面体验。
