假设你只有一台笔记本,却要同时做三件互相打架的事:在 Windows 里完成日常工作,在 Linux 上验证服务器程序,还要留一套随时可以弄坏、重装的实验环境。直接在硬盘上装三套系统当然可以,但每次切换都要重启;再准备两台电脑也能解决,只是硬件成本、耗电和维护都会跟着增加。
虚拟机给出了另一种答案:让一套软件站在硬件和操作系统之间,把一台真实计算机拆成多台看起来彼此独立的“计算机”。每台虚拟机都有自己的处理器视图、内存、磁盘和网卡,客体操作系统甚至可以不知道自己正在共享硬件。它仍然会启动内核、建立页表、安装驱动、处理中断,像管理真机一样管理这套虚拟硬件。
这件事真正困难的地方不在于画出几个虚拟设备,而在于同时满足三个互相牵制的目标:客体操作系统要像拥有整台机器那样运行;多个客体不能越过边界干扰彼此;多出来的抽象层又不能让性能掉得太厉害。理解虚拟机,就是理解虚拟机管理器怎样在“兼容、隔离、效率”之间反复取舍。
一台物理服务器有固定数量的 CPU 核心、固定容量的内存,也只有真实存在的磁盘和网卡。虚拟机管理器,也叫 VMM 或 Hypervisor,把这些资源组织成多套虚拟硬件接口。客体操作系统看到的是 vCPU、客体物理内存、虚拟磁盘控制器和虚拟网卡;真正的物理资源仍由虚拟机管理器统一控制。
这和普通进程隔离不在一个层次。普通进程接受宿主操作系统提供的系统调用接口,不能自行安装另一个内核。虚拟机则要容纳一套完整客体操作系统:客体内核会切换特权级、修改页表、配置中断控制器,还会以为自己能重启机器。虚拟机管理器必须让这些动作对客体“像真的一样”,同时保证“重启”只重启这台虚拟机,而不是把整台宿主机带走。

宿主机是提供真实 CPU、内存和设备的物理计算机。客体操作系统是运行在虚拟机里的操作系统。虚拟机管理器负责创建虚拟硬件、保存和恢复 vCPU 状态、建立内存映射、转发 I/O,并决定每台虚拟机什么时候可以使用真实资源。
“宿主操作系统”只在某些部署方式中存在。桌面虚拟化软件通常作为 Windows、macOS 或 Linux 上的应用运行,设备管理和用户界面大量借助宿主操作系统,这常被称为类型 2 或托管型虚拟化。数据中心里的虚拟机管理器更接近直接掌管硬件的系统层,常被称为类型 1 或裸金属型虚拟化。
分类能帮助我们理解部署结构,但别把它当成绝对边界。以 KVM 为例,CPU 和内存虚拟化能力在 Linux 内核中,设备模型与管理逻辑又常在用户态进程中。真实系统往往跨越“内核、用户态设备模型、管理服务”多个部分,用一句“类型 1”或“类型 2”概括不了全部实现细节。
还有一类由固件或平台硬件直接参与的逻辑分区。大型机和部分企业服务器可以把处理器、内存与 I/O 资源划给彼此隔离的分区,有些教材把它称为类型 0。它的边界更贴近硬件,性能和故障隔离更容易预测,却依赖特定平台,资源调整通常也没有纯软件虚拟机灵活。它和类型编号一样,只是理解实现位置的一种视角,不是一条适用于所有产品的统一标准。
客体写入一块虚拟磁盘时,数据最终可能落在宿主机上的一个镜像文件、逻辑卷或远程块存储中。客体向虚拟网卡发包时,数据可能经过虚拟交换机,再从物理网卡离开。客体读取时钟时,读到的时间也可能经过校正。这里的“虚拟”指的是接口与实现分离:客体按硬件接口工作,虚拟机管理器决定这个接口怎样映射到真实资源。
这种分离带来了快照、克隆、迁移和弹性分配,也增加了新的失败点。宿主机内存耗尽会连带影响多台虚拟机;虚拟交换机配错规则会让客体集体断网;虚拟磁盘的差异链损坏,影响的可能不只一个克隆。抽象层消除了客体对具体硬件的依赖,却把更多责任集中到了虚拟机管理器和管理平面。
虚拟机并不是云计算时代才出现的想法。早期大型机价格昂贵,用户又希望同时运行互相独立的系统环境,于是“把一台机器划成多台机器”很早就成为现实需求。IBM 在 20 世纪 60 年代的 CP 系列上持续探索,并在 1972 年正式推出 VM/370。后来 x86 服务器性能提高,软件二进制翻译与硬件辅助虚拟化逐步成熟,虚拟机才从大型机和专用平台扩展到普通服务器与个人电脑。
今天我们在云平台上点击“创建实例”,背后仍然是在做同一件事:选择一组虚拟硬件,准备启动镜像,把它安放到一台有足够资源的宿主机上。界面变简单了,底层的调度、地址翻译、I/O 隔离和故障恢复并没有消失。
判断一个系统是不是“整机虚拟化”,最直接的方法是看隔离边界里有没有独立内核。能在同一宿主机上同时运行不同类型的客体内核,通常是虚拟机;只隔离应用进程并共享宿主内核,通常是容器。
普通应用运行在用户态,执行危险操作时必须请求内核。客体操作系统不一样,它自己就是一个内核,天然认为自己处于最高权限。如果把客体内核毫无限制地放到真实最高特权级,那么它修改中断表、关中断或重设内存映射时,影响的就会是整台宿主机。可如果把它当普通应用运行,客体又无法完成启动和硬件管理。
虚拟机管理器要解决的核心矛盾就是:让客体保留“我有内核权限”的视角,却不真的拿到宿主机最终控制权。
最直观的办法叫陷阱与模拟。大部分普通指令可以直接在物理 CPU 上运行,例如整数加法、普通分支和访问已经映射好的内存;一旦客体执行需要控制机器全局状态的敏感操作,处理器就把控制权交给虚拟机管理器。虚拟机管理器查看退出原因,更新虚拟设备或虚拟寄存器的状态,再让客体从合适的位置继续执行。

例如,客体发出关机命令时,虚拟机管理器可以把这解释为“关闭当前虚拟机”;客体查询处理器能力时,虚拟机管理器可以返回一组经过筛选的特性;客体访问虚拟设备寄存器时,设备模型可以给出符合该设备规范的响应。客体看到的语义仍然完整,实际影响范围却被限制在自己的虚拟机内。
每次退出都不是免费的。处理器要保存客体状态、切换到虚拟机管理器、执行处理逻辑,再恢复客体状态。若一个工作负载频繁触发退出,花在来回切换上的时间就会明显增加。因此,现代硬件会尽量让常见路径直接执行,只把虚拟机管理器明确要求拦截的事件交出来。
理想的陷阱与模拟有一个前提:所有会影响虚拟化正确性的敏感指令,在权限不足时都应该可靠地陷入管理器。早期 x86 并不完全满足这个条件,有些指令在低权限运行时不会按虚拟机管理器期望的方式触发陷阱。
一种软件解法是动态二进制翻译。虚拟机管理器在客体内核代码第一次执行前检查指令块,把不能安全直接运行的指令替换成受控序列,再把翻译结果放入缓存。普通用户态代码仍可大量直接执行,翻译成本则通过缓存分摊。它证明了 x86 可以高效虚拟化,但实现复杂,需要精确保持原指令语义,还要处理自修改代码、异常位置和缓存一致性。
Intel VT-x 和 AMD-V 为处理器增加了专门的虚拟化执行机制。以概念来讲,虚拟机管理器运行在拥有最终控制权的模式,客体则运行在受控模式中;客体内部仍然可以使用自己的用户态与内核态,不必把客体内核硬塞进传统用户态。
虚拟机管理器会配置一组控制信息,决定哪些事件直接执行,哪些事件触发虚拟机退出。退出时,硬件保存客体状态并记录原因;处理完成后,通过虚拟机进入恢复客体。硬件辅助减少了软件改写指令流的负担,但没有消除虚拟化开销。退出频率、缓存扰动、中断注入和跨核调度依然会影响延迟。
如果客体内核可以修改,还可以让它明确知道自己处于虚拟环境。客体遇到需要管理器完成的特权操作时,不再假装直接控制硬件,而是通过超调用主动请求虚拟机管理器。早期 Xen 使用这条路线在缺少完整硬件辅助的机器上减少难以捕获和模拟的操作。
代价也很清楚:未经修改的操作系统不能直接使用整套接口,客体内核必须维护虚拟化适配。现代平台更常采用混合方案,让硬件辅助承担 CPU 和内存隔离,再在磁盘、网络等高频路径使用半虚拟化驱动。这里的“半虚拟化设备”只改设备交互方式,不等于整套客体内核都采用早期半虚拟化执行模型。
下面的实验把一次 vCPU 执行简化成四类操作。点击操作后,观察它是直接完成,还是经过虚拟机管理器。再切换“拦截策略”,你会看到:拦得越多,控制越细;退出次数也越多。
创建一台 4 vCPU 的虚拟机,不等于永久占有四颗物理核心。虚拟机管理器把 vCPU 当作可调度实体,再把它们放到物理 CPU 上运行。多个虚拟机的 vCPU 总数可以超过物理核心数,这叫 CPU 过量分配。只要多数虚拟机不会同时满载,空闲时间就能被复用。
代价会在高负载时暴露出来。一个 vCPU 已经可以运行,但暂时等不到物理核心,这段等待常表现为就绪时间升高。客体内部看到 CPU 很忙,未必是自己的进程真的算了那么久,也可能是 vCPU 在宿主层等调度。对延迟敏感的系统可以使用 CPU 绑定、资源预留或减少过量分配,但这样会降低整体资源利用率。
时间也需要虚拟化。客体暂停、迁移或 vCPU 被抢占后,虚拟时钟必须保持合理连续。若时钟源与同步策略不合适,定时器、日志顺序和超时判断都可能异常。现代虚拟化平台会提供面向客体的时钟机制,但高精度和实时工作负载仍需要额外校验。
普通操作系统已经在做虚拟内存:进程发出虚拟地址,客体操作系统通过自己的页表把它翻译为物理地址。进入虚拟机后,这个“物理地址”其实还不是真实内存位置,它只是客体眼中的物理地址。虚拟机管理器还要把它映射到宿主机真正的物理页。
于是,一次访问会经过三种地址:

早期软件方案可以维护影子页表,把两段映射合成为硬件能直接使用的一套映射。客体一旦修改页表,虚拟机管理器就要捕获变化并同步影子页表。它可行,却会让页表维护变得复杂,频繁更新时退出开销也很高。
现代处理器提供二级地址转换。Intel 将这类能力称为 EPT,AMD 称为 NPT。硬件在一次页表遍历中结合客体页表和二级页表完成翻译。客体可以按正常方式维护自己的页表,虚拟机管理器则控制“客体物理地址到主机物理地址”的映射。客体无法把二级页表指向别人的内存,因此这张表也是内存隔离的关键边界。
两级转换会增加最坏情况下的页表访问次数,所以 TLB 更重要。TLB 缓存近期地址翻译结果;带有虚拟机标识的缓存项还能减少切换虚拟机时的大范围刷新。大页可以提升 TLB 覆盖范围,但会降低内存分配粒度,并可能增加碎片或迁移成本。这里仍然没有“只赚不亏”的选择。
选择一个客体虚拟页,实验台会先查客体页表,再查二级页表。重复访问同一页会命中缓存;切换虚拟机后,即使虚拟地址相同,也会得到另一组映射。
管理员可以给多台虚拟机配置的内存总量超过宿主机物理内存,因为许多客体不会同时用满标称容量。这能提高部署密度,却只是利用峰值错开,并没有制造新的内存。所有客体同时增长时,虚拟机管理器必须回收、压缩或换出页面,延迟会迅速上升。
常见手段各有适用条件:
内存还受 NUMA 拓扑影响。在多路服务器上,vCPU 访问本地内存通常比跨节点访问更快。给虚拟机分了很多 vCPU,却把内存散落到远端节点,可能得到“CPU 数量更多、性能反而更差”的结果。大型虚拟机需要同时考虑 vCPU 放置和内存亲和性。
“虚拟机配置了 16 GB”只说明客体可见的容量,不保证此刻有 16 GB 独占物理内存。判断性能时要同时看预留、上限、气球回收、宿主交换和 NUMA 放置。
CPU 指令大多可以直接执行,设备访问却更麻烦。真实网卡、磁盘控制器和 GPU 的寄存器、DMA 与中断都要隔离;多个客体还要共享有限设备。I/O 路径因此常是虚拟化开销最明显的地方。

虚拟机管理器可以完整模拟一款客体认识的硬件。客体使用原本的设备驱动,寄存器访问被设备模型接收,再转成宿主 I/O。优点是兼容性强:只要客体有这款设备的驱动,就能启动和使用。缺点是路径长,频繁的寄存器访问、数据复制和退出会消耗 CPU。
设备模拟适合安装阶段、兼容旧系统或对性能要求不高的设备。它也提醒我们,虚拟机并不一定照搬宿主机真实硬件;客体看到的网卡型号可以与机房里的物理网卡完全不同。
另一条路是不再假装某款真实硬件,而是设计一套专门面向虚拟环境的高效接口。客体安装对应驱动,把请求放进共享队列,虚拟机管理器批量处理并通过较少的通知完成收发。virtio 就是常见例子。
这类方案仍然保持客体内核与虚拟机管理器的边界,只是双方约定了一种更省动作的通信方式。它通常比传统设备模拟快,也保留了较好的快照和迁移能力。代价是客体必须有合适驱动;驱动缺失时,系统可能连启动磁盘或网络都识别不到。
对高吞吐网卡、GPU 或加速器,可以把物理设备直接分配给某台虚拟机。客体使用接近原生的驱动和数据路径,减少设备模型参与。IOMMU 负责把设备发起的 DMA 限制在允许的内存范围,避免设备绕过 CPU 页表读写别的虚拟机。
直通性能好,却不等于没有代价。设备一旦专属分配,其他虚拟机就不能随意共享;宿主驱动与客体驱动的切换更复杂;迁移时还必须保存设备内部状态,并在目标主机找到兼容设备。某些设备根本不支持完整迁移。直通提高了数据路径效率,也把资源调度和可移植性变难了。
SR-IOV 试图在两者之间折中。一块物理设备可以暴露多个虚拟功能,把不同虚拟功能分给不同客体。它减少了软件交换路径,但仍受设备能力、IOMMU 隔离粒度、驱动版本和迁移支持约束。
虚拟网卡发出的帧通常先进入虚拟交换机。桥接模式让客体像局域网中的独立主机;NAT 模式借用宿主地址访问外部网络;主机专用网络则把通信限制在宿主和指定客体之间。网络能否连通不只取决于客体 IP,还取决于虚拟交换机端口、VLAN、路由、NAT、防火墙和物理网络。
排查虚拟机网络时,最好沿数据路径逐层确认:客体网卡是否启用、地址和路由是否正确、虚拟交换机端口连到哪里、宿主转发是否允许、物理网络是否接受对应地址。只在客体里反复改 IP,常常碰不到真正的故障点。
虚拟机看到的是一块按扇区读写的磁盘,宿主侧实现可以是一整个块设备,也可以是镜像文件。raw 镜像结构简单,路径短;qcow2 等格式能提供稀疏分配、压缩、后备文件和快照等能力,但要维护更多元数据。
一块标称 200 GB 的动态扩展虚拟磁盘,刚创建时可能只占很少宿主空间,随着客体写入才增长。这提高了存储利用率,也制造了新的容量视角:客体认为自己还有空闲空间,并不代表宿主存储池真的能兑现所有虚拟磁盘的剩余承诺。
如果多台虚拟机同时写满,存储池可能先耗尽。严重时,客体写入失败或被暂停。运维不能只看每台客体内部的磁盘使用率,还要监控镜像实际占用、存储池可用空间、快照增长速度和所有精简磁盘的承诺总量。
快照常用写时复制实现。创建快照时不必立即复制整块磁盘,而是固定当前映射关系;此后的新写入进入差异层,旧数据继续由原层提供。恢复快照就是重新选择一组状态。链接克隆也能共享只读基础镜像,只保存每个克隆自己的差异。

快照创建快,不代表长期使用没有成本。差异链越长,读取时需要查找的层越多;删除或合并快照可能触发大量后台 I/O;基础镜像一旦损坏,依赖它的链接克隆会一起受影响。
快照通常仍在同一存储系统上,存储池损坏时可能与虚拟机一起丢失。它适合升级前的短期回退点、可重复实验和一致性明确的维护窗口,不应代替独立位置的备份。
运行中保存磁盘还要考虑一致性。只冻结某一时刻的块设备,得到的可能只是崩溃一致状态,类似突然断电后的磁盘。数据库缓冲区和文件系统缓存里尚未落盘的数据不会自动变成应用一致状态。更稳妥的做法是让客体代理、文件系统或应用先进入可恢复状态,再创建快照。
暂停只是停止 vCPU 调度,虚拟机状态仍在内存中;保存状态会把 CPU、内存和设备状态写到持久介质,之后可以从同一位置恢复;关机则让客体操作系统完整结束运行。完整克隆复制出独立磁盘,链接克隆依赖基础镜像。操作前先弄清目标是“临时停住”“下次接着跑”“快速回退”还是“创建独立副本”,可以避免把一个功能误当成另一个。
销毁虚拟机也不只是从列表中删掉名字。虚拟磁盘、快照、固件变量、密钥、日志、备份副本、IP 地址和直通设备绑定都可能需要回收。涉及敏感数据时,还要根据底层存储特性选择安全擦除、加密密钥销毁或介质处置策略。
数据中心要维护硬件或平衡负载时,希望把虚拟机移到另一台服务器,又不长时间停止业务。难点在于虚拟机仍在运行:内存一边复制,客体一边修改页面;网络连接还要延续;目标 CPU 和设备也必须能够接住当前状态。
常见的预拷贝流程先在虚拟机继续运行时复制全部内存,然后记录复制期间被再次写过的脏页,再进行一轮或多轮增量复制。当剩余数据足够少,源主机短暂停止 vCPU,发送最后的脏页、CPU 寄存器和设备状态,目标主机恢复执行。

业务感受到的停机主要集中在最后一次停止并复制,而迁移总时长还包括前面的多轮传输。若网络每秒能传 500 MB,客体却每秒把 600 MB 内存重新写脏,待传数据不会收敛。平台只能延长迁移、限制客体写入速度、接受更长停机,或改用其他策略。
下面的实验用简化模型估算预拷贝。调整内存、有效带宽和脏页率,再点击模拟。它不会替代真实平台测量,但能直观看出:决定收敛速度的不是内存大小一个数,而是“写脏速度相对于传输速度”。
后拷贝可以先把最小运行状态送到目标主机,让 vCPU 在目标端尽快开始;缺少的页面在访问时再从源主机请求,同时后台继续传输。它避免同一页面被反复重传,却把运行中的虚拟机放在“部分内存仍依赖源主机”的状态。此时源主机或网络故障,恢复会比预拷贝更棘手。
迁移还受以下条件限制:
所以“支持实时迁移”不能只看按钮是否存在。真正的验收应包括可接受停机、负载下能否收敛、失败时回退、设备兼容和数据保护。
虚拟化让资源可以切成更细的份额,也让“卖出去的虚拟容量”大于“真正拥有的物理容量”。这正是服务器整合与云计算弹性的基础:很多业务峰值不重叠,空闲资源可以被别的虚拟机使用。
问题在于,过量分配只是统计复用。峰值同时到来时,物理上限不会因为配置页面里多写了几个 vCPU 或几 GB 内存而改变。
CPU 是时间型资源。vCPU 太多时,大家轮流等,常表现为响应变慢、抖动和就绪时间升高。内存是容量型资源,短缺时必须回收、压缩或换出,性能可能出现陡峭下降。精简存储也属于容量承诺,池耗尽后不是简单“慢一点”,而可能直接造成写入失败。
因此,不能用一个统一的“过量分配比例”替代容量规划。对批处理任务,多等一会儿可能可以接受;对交易系统,几十毫秒尾延迟就可能越过目标;对必须落盘的数据,存储池无空间更是不可接受。
设置宿主机规格和虚拟机数量,观察哪个资源先成为瓶颈。实验把平均使用率和峰值风险分开显示,提醒我们:平均值适合看利用率,容量保护要看峰值与预留。
虚拟机有独立地址空间和虚拟设备,一台客体崩溃通常不会直接破坏另一台客体。这比共享同一内核的进程级隔离边界更厚,却不能推出“虚拟机天然安全”。
虚拟机管理器、设备模拟器和管理接口本身都有代码,也可能出现漏洞。CPU 缓存、内存带宽和某些微架构状态仍由多个客体共享,既会产生噪声邻居,也可能形成侧信道。镜像里的默认密码、长期未更新的客体内核、过宽的管理权限和未加密迁移链路,都会绕过“虚拟机之间有隔离”这句宽泛结论。
更稳妥的做法是缩小管理面暴露、及时更新虚拟机管理器和客体、限制设备模型、给管理操作做身份认证与审计、隔离高敏感负载,并对备份与迁移数据使用合适保护。安全来自多层控制,不来自“我用了虚拟机”这一个标签。
这些技术都使用“虚拟”或“隔离”描述自己,但抽象的对象不同。
JVM 会加载字节码、验证代码,并通过解释或即时编译在目标平台执行。它提供的是程序执行环境,不会让 Java 程序获得一套可安装任意操作系统的虚拟硬件。容器使用命名空间等机制改变进程看到的系统视图,再用控制组限制和统计资源;容器里的进程仍然向同一个宿主内核发系统调用。
实际平台经常把这些技术叠在一起:物理服务器上运行虚拟机,虚拟机里运行容器,容器中再运行 JVM 应用。层数增加不一定错误,每层可能解决不同问题;但监控、资源限制、时钟、网络和故障定位也会跨越更多边界。选型时先问“我需要隔离的是内核、进程,还是语言运行环境”,比争论谁“更先进”有效得多。
创建虚拟机时,需要确定 vCPU 拓扑、内存容量与预留、固件类型、虚拟磁盘格式、网卡连接和启动镜像。配置过大不只浪费资源,还可能让虚拟机跨 NUMA 节点;配置过小则让客体频繁换页或排队。
运行阶段要同时观察客体和宿主。客体 CPU 使用率低但响应慢,可能是 vCPU 等待;客体剩余内存很多但宿主交换升高,可能是管理器层压力;客体磁盘延迟上升,可能来自同一存储池的其他虚拟机。只看客体内部监控,会漏掉资源真正竞争的那一层。
维护阶段包括补丁、备份恢复演练、短期快照管理、镜像更新、证书和密钥轮换。快照越积越多,差异链越长;模板长期不更新,新创建的虚拟机就会一出生便携带旧漏洞。
销毁阶段要回收计算、存储、网络和身份资源,并确保数据处理符合要求。云环境里“删除实例”后留下未挂载磁盘、快照和静态 IP,是很常见的资源泄漏。真正的生命周期管理不是把虚拟机从控制台列表中移除,而是让它留下的依赖和敏感状态都有明确去向。
虚拟机管理器做的事情,可以概括成“给客体一个完整视图,再牢牢控制视图背后的映射”。
客体执行普通 CPU 指令时尽量直接运行,敏感事件通过虚拟机退出交给管理器;客体把虚拟地址翻译成自己的物理地址,二级页表再把它限制到真正分配的主机内存;客体驱动操作虚拟设备,设备模型、共享队列或 IOMMU 把请求送向物理设备;虚拟磁盘用镜像和写时复制支持快照与克隆;迁移则把内存、处理器和设备状态在两台宿主机之间重新建立。
每一层都解决了“客体想独占、硬件要共享”的矛盾,也带来对应代价:退出有切换成本,二级翻译增加地址处理,I/O 隔离延长数据路径,快照形成依赖链,迁移受到脏页率和设备状态约束,过量分配则把低平均利用率换成高峰拥塞风险。
真正掌握虚拟化,不是记住某个产品属于哪一型,而是能沿着一次执行、一次内存访问、一次 I/O 和一次迁移追踪控制权。当你能回答“这一步由谁看到、映射到哪里、失败会影响谁、优化又牺牲了什么”,虚拟机就不再是一只神秘的黑盒。
一台 32 GB 内存的宿主机运行四台各配置 12 GB 的虚拟机。平时每台只使用 5 GB,晚间批处理时四台都迅速增长到 10 GB。请解释为什么平时看起来没有问题,晚间却可能突然变慢;至少给出两种缓解方法,并说明各自牺牲了什么。