小鱼塘--自说自话的地方

  • 小玩意
  • 小想法
记录自己技术和想法地方
  1. 首页
  2. 虚拟机技术
  3. 正文

虚拟技术理论的研究

13 8 月, 2026 5点热度 0人点赞 0条评论
内容目录

术语

host:物理主机,你的实体个人电脑
guest:虚拟主机
指令:指令集合,CPU 可以执行最小单位
二进制翻译:是将 **一个指令集架构(ISA)** 中的指令实时转换为 **另一种指令集架构** 中对应的指令,以便在不同处理器核心上执行。(分静态和动态(DBT)2种)
DBT(Dynamic Instruction Translation): 在运行时按需翻译,边执行边翻译(更常见、更灵活)
静态二进制翻译(SBT):在程序运行之前一次性完成全部翻译(如曾经的 Alpha FX!32)
指令模拟:一个指令集架构中的指令用高级语言实现,不用管用什么高级语言
VM Exit:一种由硬件自动完成的状态切换事件(Guest → Hypervisor)
VM Entry:一种由具体指令触发的状态切换操作(Hypervisor → Guest)
Hypervisor:一般是驱动实现 监听异常,用来捕获特权指令(ring 0),只有ring 0 一些特殊异常才能捕获到,ring3 无法捕获

虚拟技术种类

  1. 指令模拟、每个指令都在host 等同多个条指令执行(模拟实现),如果guest 与 host 相同CPU 指令集的话,指令会碰撞好几倍到10倍的样子,所以一般这种实现的虚拟机性能是原来的1/10
  2. 动态翻译成host 指令集合,这种集合指令翻译+动态生成指令,游戏指令无法动态执行就通过翻译执行,所以这是第一种进步,这种就看动态生成指令效率有多少,因为直接执行等于host cpu的能力,这种无需内核层支持,这种只有少部分指令才能生成
  3. 大量指令生成+少量的翻译实现,这个第二种进步,这种只有稍微特权的指令才需要翻译(模拟)实现(VT-X ,硬件虚拟化),这种intel 支持虚拟指令支持,这样子大部分指令都可以在host 直接跑,只有少部分指令需要 vmx

指令模拟

这块我目前开发nes 模拟器,基本大部分核心开发完了,所以这块技术我比较熟悉,同时重点介绍这个。

代码

比喻我实现nes cpu 模拟

    m_register.PC++;
    m_last_cpu = m_register.PC;

    switch (OPCODE) {
    case 0x69:
        R_MEM_IMM();
        ADC(bData);
        cycle = 2;
        break;
    case 0x65:
        R_MEM_MODE_ZERO_PAGE();
        ADC(bData);
        cycle = 3;
        break;
    case 0x75:
        R_MEM_MODE_ZERO_X_PAGE();
        ADC(bData);
        cycle = 4;
        break;

ADC 指令

    inline  void    ADC(BYTE data) {
        WORD result = REG_A + data + (REG_P & P_C_FLAG);
        setCarrayFlag(result);
        setZeroFlag((BYTE)result);
        setOverflowFlag((BYTE)result, data);
        setNegativeFlag((BYTE)result);
        REG_A = (BYTE)result;   //最后赋值
    }

作用:A = A + memory + C , A寄存器 + 添加内存数据+进位标志数字,本质就是一个加法,只是增加进位,同时修改各种标志位

ADC 一条指令要多条C语言模拟(后续会编译成当前机器的指令),那么编译指令就会更多,ADC 只CPU 自身执行,我们用别的语言模拟,那么一定会产生更多指令,所以为什么这种模拟性能只有1/10 左右原因,因为指令膨胀很多了。

内存地址

nes 内存地址访问

因为nes 内存没有分段也没有分片,直接裸写物理地址,我直接new 一个比64KB 就可以了,因为nes的内存寻址空间是16位就是64kb,如果内存地址寻址,那就很简单了,因为我们直接拿到物理地址,这个物理地址可以当成host的偏移,比喻我们new 内存地址 0x12345678(假设),假设访问代码访问是物理地址0x100,那么我们访问直接可以 ptr + 0x100 得到host 指针(这个就是windows的虚拟地址),然后我们直接访问就可以了

BYTE* mem_ptr = new BYTE[1024*64];

//假设物理地址:0x100
BYTE data = mem_ptr[0x100];
//这个得到对应的值

x86-64 指令内存地址访问(模拟)

这个因为引入分段(8086 引入)、后面引入分页(80386),所以我们要按照这些规则实现,因为这些用虚拟地址,不是真正地址,CPU 默认进行计算得到物理地址,得到对应值,如果我们就要模拟这些内存规则,得到物理地址后,我们再按照上面逻辑进行进行

dos 下面(因为window 95后基本废弃分段)
只是分段机制

  1. cs<< 4 + ip 得到当前指向的物理地址 (cs:ip 组合得到当前执行党的指令的地址)
  2. 得到指令 MOV AX, [1234H] (这里假设设置)
  3. 这里1234h 要加上当前段地址 ds<<4 + 1234h 得到真正物理地址,这样子去取这个地址就可以得到真正的数据了
    MOV AX, [1234H]

这里假设:
cs:1000h (代码段)
ds:2000h(数据段)
IP:100H(指令指针寄存器,指向当前指令)

当前指令计算:1000h << 4 + 100h = 10100h cpu,这里要得到指令就是前面 mov AX,[1234h]

访问1234h内存计算:因为它是访问是数据段: 2000h << 4 +1234h = 21234H
最终CPU 会在取2个字节给AX 寄存器。

为什么要分段

  1. 因为8086 寻址空间 20位,但寄存器只有16位,无法寻址到20位,所以引入cs ds ds es

分页机制
这个后面考虑,这个cpu 技术实现不一样,这块实现不一样,根据资料总结,通过指令模拟,无需内核,直接应用层就可以跑,其他的实现方式就不一样了,实现的方法就不一样了

设备模拟

  1. 显示设备模拟,这个cpu模拟后这个比较重要,通过cpu io指令模拟实现,从而对应的功能
  2. 鼠标和键盘模拟
  3. 声音设备模拟

基本都是通过IO 读写模拟,写时候把对应数据写到合适地方,读时候给出对应数据就能够模拟了,要怎么保证读取对应数据就是要考虑写时候合理解析了,比喻nes 模拟ppu(显示设备)

nes ppu 模拟 推理pc显示设备模拟

ppu 按照时钟 慢慢一行行处理显示数据,给显示缓存写数据,写满一帧数据,反复按照这个写数据,然后按照硬件逻辑显示数据

我最开始模拟PPU用最简单的方案,我不按照时钟,直接按照 60fps 读取背景和精灵数据然后直接显示出来,不管PPU 时钟,那么去掉很多逻辑,但也能跑起来一些游戏,但有的游戏就不行或者显示错乱,因为游戏严格按照时钟,游戏中间可能有变化,而我直接过滤掉了,直接最后一步导致很多逻辑没有了,导致异常。

那么模拟设备需要了解它规则,当指令模拟写完后,基本模拟设备就要找资料看设备规则有哪些,哪些是重要的,我ppu 花了很多精力上面因为好的地方没有注意,导致碰撞不准或者不触发,这个跟技术没有关系,这个跟你了解硬件的特性有关,这块算是苦力活了

讨论

  1. 指令模拟,本质也是二进制翻译(只是高级语言逻辑转换而已),如果用c/c++ 最终编译会成到对应机器码,本质一条指令转换多条指令了
  2. 为了减少指令,使用动态二进制翻译,一条指令转换对应指令(也可能多条),如果目标指令没有对应合适指令,那么可能要多条指令了,极端情况会退化成指令模拟
  3. 为了提高效率,使用静态二进制翻译,这样子不用每次翻译,直接跑指令,只有特殊指令才动态二进制翻译(这个跟AI讨论,这个动态翻译同时发展,但用的比较少)
  4. 为了提高效率,使用系统提供虚拟指令,直接不用翻译 通过硬件辅助虚拟化 或 半虚拟化,它们最终都是让客户机(虚拟机中的系统)直接跑在 CPU 上,从而完全消除了“翻译”环节。(同架构,指令集支持一样)

纯软件模拟(解释)→ 动态翻译(DBT,加速)→ 静态翻译(AOT,更快)→ 硬件直接运行 + 必要陷入(硬件辅助)
(每条指令模拟) (基本块翻译+fallback) (提前翻译+动态补齐) (无需翻译, 特殊指令捕获)

方案 代表 客户机代码如何执行 是否需要主机驱动 大致性能
纯软件全模拟 Bochs、QEMU (TCG) 每条指令都软件解释/翻译 不需要,纯用户态 慢 10~100 倍
动态二进制翻译 早期 VMware、老 VirtualBox(raw-mode) 用户态直接执行 + 内核态翻译后执行 需要(内存、中断、调度) CPU 密集接近原生,I/O 开销大
硬件辅助虚拟化 现代 VMware / VirtualBox / Hyper-V non-root 模式原生执行,敏感操作陷入 root 模式 必须(VMXON 只能在 ring 0 执行) 接近原生,EPT 后内存开销也大幅降低
技术 核心思想 如何发现敏感指令 典型应用
陷阱-模拟 (Trap-and-Emulate) 依赖硬件 硬件自动触发异常 早期虚拟化、IBM大型机
二进制翻译 (Binary Translation) VMM主动软件扫描 VMM静态/动态分析代码流 早期VMware Workstation
硬件辅助虚拟化 (Hardware-Assisted) 硬件新特性 CPU新模式自动拦截所有敏感指令 现代KVM、VMware、Hyper-V

静态翻译不是主流原因

因为很多指令无法确认到底执行哪里,比喻jmp rsi,这个只有执行时候才能确认,分支很多,无法确定哪些代码能够执行

软件代表

指令模拟:bochs
动态翻译+指令模拟:86box, virtualbox(老版本,没有硬件虚拟化) ,VMware Workstation(老版本,没有硬件虚拟化)
硬件虚拟化:比较新的版本 virtualbox VMware Workstation

这些只是测试或者使用过的软件,还有很多其他软件,因为我没有用过所以不会列出来

应用层和内核

虚拟机实现可以单纯在应用层也可以 应用层加驱动实现,原因是 x86_64 指令集 不同指令要有不同权限,所以引入ring机制, x86指令集中的特权级别控制当前在处理器上运行的程序对内存区域、I/O 端口和特殊指令等资源的访问

ring0 权限最高 ring3 权限最低, 一些特殊指令只能在ring 0 ,如果在应用层(ring 3 ,非驱动程序一般都是在这一层)

驱动的工作:驱动承担了 CPU 调度、内存虚拟化、中断注入、设备 I/O 模拟等关键任务,这些任务都必须在 ring 0 完成。

虚拟机怎么处理特殊指令呢?

ring 0 实现

  1. 如果技术实现二进制翻译,检测到特权指令,直接模拟指令实现
  2. 硬件虚拟化,直接捕获异常即可
Guest (Non-root) ──[特权指令]──► VM Exit ──► Hypervisor (Root)
                                              │
                                              ▼
Guest (Non-root) ◄── VM Entry ◄── 处理完成 ◄──┘

gp捕获

这个捕获必须在ring 0,那么必须实现驱动,从而 trap and emulate

IDT :中断描述表,每项是对应的函数,默认情况下

trap → 通过 IDT 转发给 VMM → 解码指令 → 模拟执行/翻译

虚拟化的区别

  1. 全虚拟化,guest 不知道自己在虚拟机里面,所有代码直接执行
  2. 半虚拟化,需改gutest os的特权代码,避免二进制翻译(这样子不用二进制翻译),只能支持开源代码,这样子需要重新编译开发自己的代码进行,所以linux 是可以走这个技术,而windows 系统是无法使用该技术

怎么进入guest os的指令

这里讨论二进制翻译指令

// 伪代码:翻译缓存中的代码块
typedef void (*TranslatedBlockFunc)(void);

// 执行翻译后的代码块
TranslatedBlockFunc tb_func = (TranslatedBlockFunc)tb_host_address;
tb_func();  // 直接调用,CPU 开始执行 Host 机器码

FlushInstructionCache:刷新指令缓存,但细节我暂时没有理解

guest 与 host 指令集一样

如果一样,可以直接跑指令,但特权指令肯定不能调用,所以这里还是必须二进制指令翻译,主要编译成代码块,用于缓存,这样子不用每次翻译,增加速度,特权指令不用翻译,直接跑,因为可以抛出异常给 Hypervisor 处理,但一些临界指令,它不会抛出错误,但权限不够不能执行,这样子导致无法捕获,那么就特殊处理,只能二进制翻译

todo

后续可能修改文章和补充文章
我没有完全虚拟机开发经验,只是为了技术研究,跟AI讨论,总结分析得出一些所谓的心得

资料

这个文章不错
https://blog.csdn.net/fearhacker/article/details/152839626

标签: 虚拟机
最后更新:13 8 月, 2026

小鱼儿

爱研究技术,爱玩LOL

点赞

COPYRIGHT © 2022 小鱼塘. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

湘ICP备18005349号