第一百九十六章:虚拟化之道
大乘期涉及内核源码:
一
林小源离开容器的平原,来到一片深邃的地底洞穴。洞穴的穹顶极高,上面镶嵌着无数星辰般的光点——每一个光点都是一个虚拟机。
洞穴中央矗立着一座巨大的熔炉,炉火通明。熔炉旁站着一个魁梧的铁匠,赤裸的上身布满伤疤,双手握着一把沉重的锤子,正在锻造一块金属。
"你来找 KVM?"铁匠头也不抬地问。
"你是 KVM?"
"我是内核的虚拟化模块。"铁匠把锤子砸在砧板上,火星四溅,"我把 Linux 变成了 hypervisor。每一个虚拟机,在我眼里就是一个进程。"
林小源抬头看着穹顶上的光点,每一个都散发着独立的光芒,互不干扰。"它们都有自己的内核?"
"对。"铁匠说,"全虚拟化——虚拟机不知道自己被虚拟化。CPU 支持 VT-x 和 AMD-V,硬件帮忙隔离。半虚拟化——虚拟机知道自己在虚拟环境里,修改了内核,直接和我通信,效率更高。"
/*
* 虚拟化类型:
*
* 1. 全虚拟化
* 虚拟机不知道被虚拟化
* 硬件辅助 (VT-x, AMD-V)
*
* 2. 半虚拟化
* 虚拟机知道被虚拟化
* 修改客户机内核
*
* 3. 硬件辅助虚拟化
* CPU 支持虚拟化
* VT-x (Intel), AMD-V (AMD)
*
* KVM (Kernel-based Virtual Machine):
* 内核模块
* 把 Linux 变成 hypervisor
* 每个虚拟机是一个进程
*
* KVM 架构:
* 宿主机内核 (KVM 模块)
* ↓
* 虚拟机进程 (QEMU)
* ↓
* 客户机内核
*
* 虚拟化硬件:
* VT-x — CPU 虚拟化
* EPT — 内存虚拟化
* SR-IOV — I/O 虚拟化
*
* 性能优化:
* virtio — 半虚拟化驱动
* huge pages — 大页
* NUMA 感知
*/
/* 模拟 VMCS (Virtual Machine Control Structure) */
struct vmcs {
int vmx_basic;
int vmcs_revision;
int guest_rip;
int guest_rsp;
int host_rip;
int host_rsp;
int vm_entry;
int vm_exit;
};
void print_vmcs(struct vmcs *vmcs) {
printf(" VMCS revision: %d\n", vmcs->vmcs_revision);
printf(" Guest RIP: 0x%x\n", vmcs->guest_rip);
printf(" Guest RSP: 0x%x\n", vmcs->guest_rsp);
printf(" Host RIP: 0x%x\n", vmcs->host_rip);
printf(" Host RSP: 0x%x\n", vmcs->host_rsp);
}
printf("=== 虚拟化之道 — 硬件隔离 ===\n\n");
printf("虚拟化让一个物理机运行多个虚拟机:\n\n");
printf("--- 虚拟化类型 ---\n");
printf("全虚拟化:\n");
printf(" 虚拟机不知道被虚拟化\n");
printf(" 硬件辅助\n\n");
printf("半虚拟化:\n");
printf(" 虚拟机知道被虚拟化\n");
printf(" 修改客户机内核\n\n");
printf("硬件辅助虚拟化:\n");
printf(" CPU 支持\n");
printf(" VT-x, AMD-V\n\n");
printf("--- KVM 架构 ---\n");
printf("宿主机内核 (KVM 模块)\n");
printf(" ↓\n");
printf("虚拟机进程 (QEMU)\n");
printf(" ↓\n");
printf("客户机内核\n\n");
printf("--- VMCS ---\n");
struct vmcs vmcs = {
.vmx_basic = 0x10,
.vmcs_revision = 1,
.guest_rip = 0x1000,
.guest_rsp = 0x7fff0000,
.host_rip = 0xffffffff81000000,
.host_rsp = 0xffff880000000000,
.vm_entry = 0,
.vm_exit = 0,
};
print_vmcs(&vmcs);
printf("\n--- 虚拟化硬件 ---\n");
printf("VT-x (Intel):\n");
printf(" CPU 虚拟化\n");
printf(" VMX root/non-root\n\n");
printf("EPT (Extended Page Tables):\n");
printf(" 内存虚拟化\n");
printf(" 两层页表\n\n");
printf("SR-IOV:\n");
printf(" I/O 虚拟化\n");
printf(" 硬件直接分配\n\n");
printf("--- 性能优化 ---\n");
printf("virtio:\n");
printf(" 半虚拟化驱动\n");
printf(" 高效 I/O\n\n");
printf("huge pages:\n");
printf(" 大页\n");
printf(" 减少 TLB 缺失\n\n");
printf("NUMA 感知:\n");
printf(" 本地内存访问\n\n");
printf("--- 容器 vs 虚拟机 ---\n");
printf("容器:\n");
printf(" 共享内核\n");
printf(" 轻量\n");
printf(" 启动快\n\n");
printf("虚拟机:\n");
printf(" 独立内核\n");
printf(" 强隔离\n");
printf(" 适合不同 OS\n");#include <stdio.h>
/*
* 虚拟化类型:
*
* 1. 全虚拟化
* 虚拟机不知道被虚拟化
* 硬件辅助 (VT-x, AMD-V)
*
* 2. 半虚拟化
* 虚拟机知道被虚拟化
* 修改客户机内核
*
* 3. 硬件辅助虚拟化
* CPU 支持虚拟化
* VT-x (Intel), AMD-V (AMD)
*
* KVM (Kernel-based Virtual Machine):
* 内核模块
* 把 Linux 变成 hypervisor
* 每个虚拟机是一个进程
*
* KVM 架构:
* 宿主机内核 (KVM 模块)
* ↓
* 虚拟机进程 (QEMU)
* ↓
* 客户机内核
*
* 虚拟化硬件:
* VT-x — CPU 虚拟化
* EPT — 内存虚拟化
* SR-IOV — I/O 虚拟化
*
* 性能优化:
* virtio — 半虚拟化驱动
* huge pages — 大页
* NUMA 感知
*/
/* 模拟 VMCS (Virtual Machine Control Structure) */
struct vmcs {
int vmx_basic;
int vmcs_revision;
int guest_rip;
int guest_rsp;
int host_rip;
int host_rsp;
int vm_entry;
int vm_exit;
};
void print_vmcs(struct vmcs *vmcs) {
printf(" VMCS revision: %d\n", vmcs->vmcs_revision);
printf(" Guest RIP: 0x%x\n", vmcs->guest_rip);
printf(" Guest RSP: 0x%x\n", vmcs->guest_rsp);
printf(" Host RIP: 0x%x\n", vmcs->host_rip);
printf(" Host RSP: 0x%x\n", vmcs->host_rsp);
}
int main() {
printf("=== 虚拟化之道 — 硬件隔离 ===\n\n");
printf("虚拟化让一个物理机运行多个虚拟机:\n\n");
printf("--- 虚拟化类型 ---\n");
printf("全虚拟化:\n");
printf(" 虚拟机不知道被虚拟化\n");
printf(" 硬件辅助\n\n");
printf("半虚拟化:\n");
printf(" 虚拟机知道被虚拟化\n");
printf(" 修改客户机内核\n\n");
printf("硬件辅助虚拟化:\n");
printf(" CPU 支持\n");
printf(" VT-x, AMD-V\n\n");
printf("--- KVM 架构 ---\n");
printf("宿主机内核 (KVM 模块)\n");
printf(" ↓\n");
printf("虚拟机进程 (QEMU)\n");
printf(" ↓\n");
printf("客户机内核\n\n");
printf("--- VMCS ---\n");
struct vmcs vmcs = {
.vmx_basic = 0x10,
.vmcs_revision = 1,
.guest_rip = 0x1000,
.guest_rsp = 0x7fff0000,
.host_rip = 0xffffffff81000000,
.host_rsp = 0xffff880000000000,
.vm_entry = 0,
.vm_exit = 0,
};
print_vmcs(&vmcs);
printf("\n--- 虚拟化硬件 ---\n");
printf("VT-x (Intel):\n");
printf(" CPU 虚拟化\n");
printf(" VMX root/non-root\n\n");
printf("EPT (Extended Page Tables):\n");
printf(" 内存虚拟化\n");
printf(" 两层页表\n\n");
printf("SR-IOV:\n");
printf(" I/O 虚拟化\n");
printf(" 硬件直接分配\n\n");
printf("--- 性能优化 ---\n");
printf("virtio:\n");
printf(" 半虚拟化驱动\n");
printf(" 高效 I/O\n\n");
printf("huge pages:\n");
printf(" 大页\n");
printf(" 减少 TLB 缺失\n\n");
printf("NUMA 感知:\n");
printf(" 本地内存访问\n\n");
printf("--- 容器 vs 虚拟机 ---\n");
printf("容器:\n");
printf(" 共享内核\n");
printf(" 轻量\n");
printf(" 启动快\n\n");
printf("虚拟机:\n");
printf(" 独立内核\n");
printf(" 强隔离\n");
printf(" 适合不同 OS\n");
return 0;
}二
铁匠从熔炉里取出一块灼热的金属,放在砧板上。那金属的表面不断闪烁——一面刻着 "VMX root",另一面刻着 "VMX non-root"。
"这是 CPU 的两种模式。"铁匠说,"VMX root 模式运行 hypervisor——也就是我。VMX non-root 模式运行虚拟机。虚拟机执行敏感指令时,CPU 自动切换回 root 模式,由我处理。"
"每次切换都有开销吧?"林小源问。
"当然。"铁匠点头,"VM exit 和 VM entry 各需要几百个 CPU 周期。所以能避免切换就避免——virtio 就是这个道理。"
他锤了一下那块金属:"每个虚拟机都是一个进程。我用 VMCS——虚拟机控制结构——记录虚拟机的状态:Guest RIP、Guest RSP、Host RIP、Host RSP。虚拟机运行时,CPU 在 non-root 模式;VM exit 时,CPU 切回 root 模式,我读 VMCS 知道发生了什么,处理完再切回去。"
三
铁匠把锻好的金属浸入一桶冷水中,蒸汽腾起。他从水里捞出一块闪闪发亮的薄片——上面刻着 "virtio"。
"半虚拟化驱动。"铁匠说,"全虚拟化里,虚拟机的每个 I/O 操作都要经过 VM exit,由 QEMU 模拟设备,再 VM entry 回去。太慢了。"
他把薄片递给林小源:"virtio 不一样。虚拟机知道自己在虚拟环境里,直接通过共享内存和我通信,不需要 VM exit。I/O 性能接近原生。"
林小源接过薄片,感受到它的轻薄——几乎没有重量,却异常坚固。
"还有 huge pages。"铁匠说,"大页减少 TLB 缺失,内存访问更快。NUMA 感知让虚拟机尽量访问本地内存节点。这些细节加起来,虚拟机的性能就能逼近物理机。"
林小源抬头看着穹顶上的光点。每一个光点都是一个独立的世界,共享同一台物理机,却互不干扰。这就是虚拟化之道——硬件级别的隔离。
道藏笔记
内核启示
虚拟化让一个物理机运行多个虚拟机。
虚拟化类型:
- 全虚拟化 — 硬件辅助
- 半虚拟化 — 修改客户机内核
- 硬件辅助 — VT-x, AMD-V
KVM 架构:
- 内核模块
- 每个虚拟机是一个进程
- VMX root/non-root
虚拟化硬件:
- VT-x — CPU 虚拟化
- EPT — 内存虚拟化
- SR-IOV — I/O 虚拟化
性能优化:
- virtio — 半虚拟化驱动
- huge pages — 大页
- NUMA 感知
虚拟化是隔离——硬件级别的隔离。
虚拟化之试
虚拟化性能优化中,为减少 TLB 缺失、提升内存访问效率,本章提到什么大页方案?