09-裸机hypervisor的系统组织
裸机 hypervisor 的系统组织——无 OS 时系统如何自组装
裸机 VT(Intel VT-x)程序没有 Linux 的 init、没有 RTOS 的调度器、没有 Qt 的事件循环——它必须自己建立整个系统:从硬件抽象接口开始,把 CPU/VMX/Memory/EPT/Interrupt/Communication/Hook 组织成子系统,再由
hypervisor_main()完成初始化与组装。这一条线是「系统组织」最纯粹的案例:没有操作系统替你组装,一切组装责任都落在程序自己身上。同时它展示了两个重要方法:MVP 增量开发(先跑通最小闭环再逐步加子系统)与结构/流程/状态三维理解(只看目录看不到系统怎么运行)。
一、裸机 VT 程序本身就是一个小型系统
先不要从「代码文件夹」开始,而是从系统职责开始。假设是 x86-64 Intel VT-x 裸机 hypervisor(EFI/bootloader 进入后自己建立 VMX 环境,最终启动 Windows/Linux guest):
1 | VT Hypervisor |
这些不能全部叫「层」——它们更准确地说是子系统(Memory、VMX、EPT、Interrupt、VCPU 是并列的系统能力)。
最底层:硬件抽象接口
裸机没有 Linux/Windows 提供 malloc/thread/socket/driver,所以首先要自己建立最基本的硬件接口:
1 | Platform |
1 | cpu.read_msr(); cpu.write_msr(); |
MSR、CR0/CR3/CR4、CPUID、APIC、页表、物理内存是硬件/CPU 能力;read_msr() / write_msr() / allocate_page() / map() 是你封装出来的接口。
二、子系统拆解
VMX 子系统
1 | VMX:VMXON / VMCS / VM Entry / VM Exit / VMX Controls / VCPU |
这是一个完整的虚拟化子系统。
Memory / EPT 子系统
1 | Memory:Physical Memory / Page / Page Table / EPT / Mapping / Memory Pool |
EPT 本身又可以拆成组件:EPT PML4 / PDPT / PD / PT / Entry,而 ept.map() / ept.unmap() / ept.translate() 是对这些组件封装出来的接口——完全对应「组件 → 封装 → 接口」。
VM Exit 子系统
1 | VM Exit |
这里就出现前面一直在讨论的「解析 → 分发 → 处理」:
1 | VM Exit → 读取 VMCS Exit Reason → Dispatcher → EPT Violation Handler → Memory/Hook Service |
注意:Dispatcher 是 VM Exit 子系统中的组件/机制,而不是整个 VT 系统的「控制器层」。
Hook 子系统(VT Debugger/Protector)
1 | Hook:EPT Hook / Execute Hook / Read Hook / Write Hook / Page Permission / Hook Manager |
「让程序运行后不能被读写」可以成为一个应用功能/策略,而不是直接塞进 EPT 实现里。
Communication 子系统
Windows 驱动或其他程序要控制 hypervisor:
1 | Communication:Transport / Message / Command / Request / Response / Dispatcher |
READ_MEMORY / WRITE_MEMORY / PROTECT_PAGE / HOOK / UNHOOK 属于上层功能。
Service 层
1 | Service:MemoryService / ProcessService / HookService / DebugService / ProtectionService |
Service 是使用多个底层子系统完成业务功能的地方——与服务器分析的结论一致。
完整 VT 系统结构
1 | VT Hypervisor |
三、谁组装?——裸机最重要的问题
裸机没有 Linux init / RTOS scheduler / Qt event loop,所以必须自行有一个系统启动/组装流程:
1 | EFI → Hypervisor Entry → CPU 初始化 → Memory 初始化 → VMX 初始化 |
1 | int hypervisor_main() |
hypervisor_main() 就非常接近一直在寻找的那个「Server.cpp 到底在哪里?」——它就是裸机系统的组装入口(Composition Root)。
不要把所有东西都叫「层」
1 | VT System |
每个子系统内部:Subsystem → Components → Interfaces。最后由 hypervisor_main() 完成初始化 + 组装。
VT Hypervisor 是系统;VMX、EPT、Memory、Interrupt、Communication、Hook 是子系统;子系统内部由组件构成;组件通过接口暴露能力;Service 负责跨子系统组织业务;启动入口负责初始化和组装整个系统。
四、算法属于子系统内部,不是独立层
VT 和 Linux 都有大量算法,但通常不会单独放一个 Algorithm/ 目录:
1 | EPT → 页表建立/遍历/地址转换算法 |
它们通常属于相应子系统内部。这与「功能 = 算法 + 数据结构 + 接口」不冲突——两个维度不同:
1 | 系统如何组织:System → Subsystem → Component |
假设 Memory 子系统:
1 | 组件:Page / PageTable / MemoryPool / EPT |
它们共同构成 Memory Subsystem。
算法是实现功能的组成部分,不一定是系统架构中的独立层。 这就是为什么 Linux 和 VT 裸机都有大量算法,但没有必要存在一个统一的
Algorithm层。
五、结构 / 流程 / 状态 三维理解
对于裸机 VT 这种系统程序,这样理解比单纯看「分层目录」更准确。
1. 生命周期(启动 → 运行 → 结束)
1 | 启动 → Boot → Platform 初始化 → Memory 初始化 → VMX 初始化 |
回答:这个系统从出生到死亡经历什么阶段?
2. 运行中的流程(一次事件怎么走)
1 | Guest Running → VM Exit → 读取 Exit Reason → Dispatcher → 对应 Handler |
回答:系统运行的时候,一件事情经过哪些组件? 这与服务器的「接收 → 解析 → 分发 → 调用功能」是同一类动态处理链。
3. 状态变化
1 | VCPU: Created → Initialized → Running → VM Exit → Handling → VM Entry → Running |
回答:某个对象/子系统的状态怎么变化?
三者合起来
1 | 系统 |
VT 程序本质上是一个持续运行的状态机(Hypervisor Running ↔ Guest Running ↔ VM Exit ↔ Handling ↔ VM Entry),只看目录只能知道「有什么」,不知道「什么时候初始化、谁先初始化、谁触发 VM Exit、处理完进入哪里」——这些必须通过生命周期 + 运行流程 + 状态变化理解。
以后分析陌生大型程序的四个问题
1 | ① 结构:系统由什么组成?子系统是什么?组件是什么? |
结构看「有什么」,接口看「怎么连接」,流程看「怎么运行」,状态看「怎么变化」。 对于裸机 VT,动态流程甚至比目录分层更重要。
六、MVP 增量开发:先跑通最小闭环,再逐步加子系统
MVP 不应该先按目录做
先定义最小生命周期:
1 | 启动 → 初始化 → 进入运行 → 处理一次事件 → 继续运行 → 退出 |
最小 VT MVP 具体化:
1 | EFI → Hypervisor Entry → CPU/VMX 初始化 → 建立最小 VMCS → 启动 Guest |
这已经是一个完整的 VT 系统闭环。
MVP-0:第一版可以极其小
1 | Boot(Entry) |
先证明 VT 的基本闭环成立。 此时根本不需要 EPT / Communication / Hook / Windows Driver / Process / Debug / Protection。
逐步添加子系统
1 | MVP-0:VMX 闭环 |
每次增加子系统都遵循一个原则
新增一个子系统,不只是把代码目录加进去,而是要把它接入生命周期和执行流。
例如加入 Communication,不能只是 Communication/ command.cpp + dispatcher.cpp,而应该同时增加:
1 | 启动流:Boot → Communication::initialize() → Running |
每个子系统至少要回答:
1 | 1. 谁初始化我? |
最终得到的是「逐步生长的系统」
1 | MVP 0: VMX → Guest ↕ VM Exit |
这就是一种非常典型的 Vertical Slice(垂直切片)式增量开发:
不是先把每一层全部写完,再开始运行;而是先让一个最小的端到端执行链跑起来,然后不断向这个闭环中增加能力。
每个阶段维护三份东西
1 | MVP |
第一阶段:
1 | 生命周期:Start → Init → Run → Exit |
第二阶段加入 EPT,这三张图一起扩展。
系统不是把子系统放进目录里就完成了,而是把子系统接入生命周期、执行流和状态机,整个东西才真正成为一个可运行的系统。
七、启动流程与 EFI 组装
先纠正一个关键点
如果 VT 裸机程序真的
return回 UEFI,那么它自己就结束了,不可能同时继续驻留并控制随后启动的操作系统。
要做的是:
1 | UEFI → 加载VT EFI/bootloader → 初始化 VT → 准备 Guest OS |
而不是:
1 | UEFI → VT → return → UEFI → Windows (VT 已经退出,不可能控制 Windows) |
MVP 的真实启动流
1 | UEFI Firmware |
这才是真正的 UEFI → Hypervisor → Guest OS 生命周期。
代码目录(MVP)
1 | hypervisor/ |
关键不是目录,而是 boot/efi_main.cpp → main.cpp → vmx/ → guest/ 形成一条真正的启动执行流。
最外层的 EFI 入口
1 | EFI_STATUS EFIAPI efi_main( |
boot/efi_main.cpp 不是 VMX 子系统,它负责把 UEFI 提供的启动环境转换成 Hypervisor 可以运行的环境。
系统组装入口
1 | int hypervisor_main(const BootInfo& boot) |
这就是前面一直在寻找的「Server.cpp 那种组装整个系统的代码」——System Composition Root(系统组装入口)。
VMXON 在哪里?属于 VMX 子系统
vmx::initialize() 内部才执行具体 VMX 初始化:
1 | void vmx::initialize() |
hypervisor_main() 不应该自己写一堆 VMXON 细节,它只负责 vmx::initialize();——这就是「组装和组件实现分开」。
VMCS 在哪里?
VMXON 成功后:VMXON → VMCS 创建 → VMCS 初始化(Guest State / Host State / Execution Controls / Exit Controls / Entry Controls)。
1 | void Vmcs::initialize(...) |
组装关系:hypervisor_main() → vmx::initialize() → vmx::create_vmcs() → guest::initialize() → vmx::launch_guest()
.bin → .img 到底是什么关系?
1 | hypervisor.bin = 一段机器代码 |
不是 bin 直接变成 img,而是:
1 | C/C++/ASM → Compile → Object → Link → EFI executable/binary |
UEFI 固件看到 EFI/BOOT/BOOTX64.EFI 之后才能把它作为 UEFI application 加载。
最适合 MVP 的 Guest
1 | guest_start: |
甚至专门让它触发一个 VM Exit(比如 CPUID),验证 UEFI → Boot → VMX → VMCS → VMLAUNCH → Guest → VM Exit → Handler → VMRESUME → Guest 整个生命周期成立。
迭代路线
1 | MVP 0: 最小 Guest(验证 VMXON/VMCS/VM Entry/VM Exit) |
如果目标最终是「VT 启动后接管并启动现有 Windows」,应该把它作为后续阶段单独设计;MVP 最好先用一个极小 Guest 把 UEFI → VMX → VM Entry → VM Exit → VM Resume 的完整闭环跑通。
八、这一条线的位置
1 | 拆解与组织(主题): |
与最小闭环主题的关系:MVP-0「先证明基本闭环成立」就是最小闭环——最小输入(启动)+ 处理(VMXON/VM Entry)+ 输出(Guest 运行/VM Exit)+ 验证(能恢复 Guest)= 一个可运行验证的闭环。之后每一步都是闭环半径的扩大。
收束
1 | 裸机 VT = 最纯粹的系统组织案例: |
裸机 hypervisor 的系统组织 = 无 OS 时系统自组装:子系统拆解 + hypervisor_main 组装入口 + MVP 增量开发 + 启动链。 它是「拆解与组织」「最小闭环」「入口组装」三条原则在极端环境(没有任何操作系统帮助)下的完整验证。
