Skip to content

第一百九十六章:虚拟化之道

大乘期

涉及内核源码:

林小源离开容器的平原,来到一片深邃的地底洞穴。洞穴的穹顶极高,上面镶嵌着无数星辰般的光点——每一个光点都是一个虚拟机。

洞穴中央矗立着一座巨大的熔炉,炉火通明。熔炉旁站着一个魁梧的铁匠,赤裸的上身布满伤疤,双手握着一把沉重的锤子,正在锻造一块金属。

"你来找 KVM?"铁匠头也不抬地问。

"你是 KVM?"

"我是内核的虚拟化模块。"铁匠把锤子砸在砧板上,火星四溅,"我把 Linux 变成了 hypervisor。每一个虚拟机,在我眼里就是一个进程。"

林小源抬头看着穹顶上的光点,每一个都散发着独立的光芒,互不干扰。"它们都有自己的内核?"

"对。"铁匠说,"全虚拟化——虚拟机不知道自己被虚拟化。CPU 支持 VT-x 和 AMD-V,硬件帮忙隔离。半虚拟化——虚拟机知道自己在虚拟环境里,修改了内核,直接和我通信,效率更高。"

c
/*
 * 虚拟化类型:
 *
 * 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");

铁匠从熔炉里取出一块灼热的金属,放在砧板上。那金属的表面不断闪烁——一面刻着 "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 缺失、提升内存访问效率,本章提到什么大页方案?

答对后才能继续滑动和进入下一章。

以修仙之名,悟内核之道