在 Apple Silicon 平台上运行虚拟机的开发者通常会遇到一个令人困惑的限制:无论使用 UTM、Parallels 还是 VMware Fusion,基于 M 系列芯片的 Mac 主机同时只能运行最多 2 个 macOS 客户机虚拟机。这一限制并非单纯的硬件约束,而是苹果软件许可协议与内核实现共同作用的结果。本文将从授权条款、内核实现、虚拟化架构三个维度进行深入分析,并给出可落地的工程突破方案。

限制的表层:许可协议而非硬件天花板

苹果 macOS 软件许可协议(Software License Agreement,SLA)第 2.B.iii 条明确规定:在每台已经运行苹果软件的 Apple 品牌计算机上,用户可以安装、运行最多两个额外的 macOS 副本或实例,用于软件测试、开发、运行 macOS Server 或个人非商业用途。这一条款适用于所有虚拟化环境,包括 Apple 自家的 Virtualization.framework 以及第三方虚拟化解决方案。

从工程角度看,这一限制在 Virtualization.framework 中表现为 VZError.Code.virtualMachineLimitExceeded 错误。当用户尝试启动第三个 macOS 虚拟机时,系统会抛出「The number of virtual machines exceeds the limit. The maximum supported number of active virtual machines has been reached」的错误信息。值得注意的是,这一限制仅针对 macOS 客户机;运行 Linux 或 Windows ARM 版虚拟机不受此约束,因为这些操作系统不受苹果 SLA 的管辖。

内核实现:从用户态框架到 XNU 深水区

早期社区普遍认为 2 VM 限制硬编码在 Virtualization.framework 的用户态代码中,因此尝试在 /System/Library/Frameworks/Virtualization.framework 目录下寻找相关字符串或函数。然而,通过对 macOS Big Sur 及后续版本的 dyld 共享缓存进行逆向分析,研究人员发现苹果将这一限制的实现位置放在了 XNU 内核而非用户态框架。

具体而言,限制逻辑位于 XNU 内核的 hv_vm_* 函数族中。核心变量 hv_apple_isa_vm_quota 负责追踪当前活跃的虚拟机数量。每当创建新虚拟机时,内核调用 hv_trap_vm_create 函数对该变量进行递减操作;每当虚拟机销毁时,则通过 hv_vm_destroy_0 函数恢复计数。这种设计将限制检查下沉到内核特权级,使得普通用户态应用无法直接绕过。

在 ARM 架构层面,Apple Silicon 利用 EL2(Exception Level 2)实现 hypervisor 功能。EL2 提供了 stage-2 页表转换(也称为二级地址转换),允许 hypervisor 拦截客户机的敏感指令并实现内存虚拟化。苹果的 Virtualization.framework 正是运行在 EL2 之上的轻量级 hypervisor,通过 hypervisor.framework 和 VZVirtualMachine 等 API 向用户态暴露虚拟化能力。

工程突破:绕过内核限制的实践路径

尽管苹果将 VM 配额检查嵌入 XNU 内核,但并未完全封死技术探索的空间。通过对 macOS Sonoma 及更早版本内核的逆向分析,研究人员发现了两个关键的启动参数:hypervisor=hv_apple_isa_vm_quota=。前者作为门控开关,后者用于覆盖内核中的配额默认值。

在开发版内核(development kernel)中,hv_apple_isa_vm_quota 参数可以直接通过启动参数传递,数值范围可达 0x7FFFFFFF(理论最大约 21 亿虚拟机)。然而,在正式版内核中,苹果添加了 AppleInternal 检查机制 —— 只有当系统启用了 AppleInternal 模式或运行开发版内核时,hv_apple_isa_vm_quota 参数才会被内核解析。AppleInternal 模式通常与系统完整性保护(System Integrity Protection,SEP)以及 csrutil 配置关联。

实现突破的完整工程路径如下:首先,从 Apple Developer Portal 下载与当前 macOS 版本匹配的 Kernel Debug Kit(KDK),确保内核变体(variant)一致,例如 M2 Pro 机型对应 T6020;随后,使用 kmutil 工具创建自定义内核集合(Kernel Collection),指定 --variant-suffix development 以生成开发版内核;最后,在恢复模式(Recovery OS)下执行以下操作 —— 禁用 SIP、禁用启动参数限制、配置自定义内核集合,并设置启动参数 kcsuffix=development hypervisor=0x1 hv_apple_isa_vm_quota=0xFF

完成上述配置后重启系统,即可在 M2 Pro MacBook Pro 上同时运行 9 个乃至更多 macOS 虚拟机。实测表明,在 M2 Pro(10 核 CPU + 16 GB 统一内存)配置下,9 个虚拟机同时运行仍可保持基础操作响应,但 CPU 与内存资源会被大量消耗,风扇转速会显著上升。

局限性与监控要点

需要明确的是,绕过 2 VM 限制属于未公开支持的实验性操作,存在以下工程风险:其一,内核更新后可能破坏自定义内核集合的兼容性,每次 macOS 重大升级都需要重新构建内核集合;其二,AppleInternal 检查机制可能在未来版本中进一步加强,使得绕过难度显著提升;其三,苹果明确在 SLA 中限制 2 VM 实例,商业场景下的合规性风险需由用户自行承担。

对于需要监控虚拟化资源使用的运维场景,以下参数值得关注:使用 sysctl kern.osbuildconfig 验证当前内核类型;通过 nvram boot-args 确认启动参数已生效;在客户机内部,可通过 vm_stathost_statistics API 监控内存压力。需要特别注意的是,由于所有虚拟机共享统一内存架构,过度分配会导致系统整体可用内存急剧下降,建议为每个虚拟机设置不超过主机总内存 20% 的分配配额,并在最坏情况下保留至少 4 GB 物理内存供主机操作系统使用。


资料来源

  • Khronokernel: 《Apple Silicon and Virtual Machines: Beating the 2 VM Limit》(2023 年 8 月)
  • Apple Developer Documentation: VZError.Code.virtualMachineLimitExceeded
  • Apple: macOS Ventura Software License Agreement, Section 2.B.iii