UTM 深度解析:基于 QEMU 的 iOS/macOS 全功能系统模拟器与虚拟机主机
2026/9/19 4:56:01 网站建设 项目流程

UTM 深度解析:基于 QEMU 的 iOS/macOS 全功能系统模拟器与虚拟机主机

【免费下载链接】UTMVirtual machines for iOS and macOS项目地址: https://gitcode.com/gh_mirrors/ut/UTM

UTM 是一款面向 iOS 与 macOS 的全功能系统模拟器(System Emulator)和虚拟机主机(VM Host),其模拟与虚拟化引擎基于 QEMU,允许用户在 Mac、iPhone 和 iPad 上运行 Windows、Linux、macOS 等多种客户机系统。本文以仓库根目录的 README.zh-Hans.md 为主线,结合仓库源码与官方开发文档,系统讲解 UTM 的核心特色、UTM SE 的实现原理、整体架构分层、安装渠道以及从源码构建与打包的完整流程,帮助读者既理解"它能做什么",也掌握"它如何实现、如何构建"。

一、项目定位与核心能力

UTM 的全称来自通用图灵机(Universal Turing Machine),README 开篇引用了艾伦·图灵 1936 年的论述:"发明一台可用于计算任何可计算序列的机器是可行的。"(It is possible to invent a single machine which can be used to compute any computable sequence.),以此点明项目的本质——用一台设备模拟出可运行任意操作系统的通用计算环境。

从技术形态看,UTM 是"系统级模拟器 + 虚拟机主机"的复合体:它不只模拟 CPU 指令集,还模拟 MMU、各类设备等完整硬件(即 README 中所说的"使用 QEMU 的全系统模拟(MMU、设备等)"),因此客户机中的操作系统无需任何修改即可运行。在 UTMQemuSystem.m 中可以看到 UTM 直接以动态库方式嵌入 QEMU,通过_qemu_init_qemu_main_loop_qemu_cleanup三个函数指针驱动 QEMU 的完整生命周期(对应startQemu流程,见 UTMQemuSystem.m),这与"QEMU 是 UTM 的骨干引擎"的定位完全一致。

核心特色清单(README 原文)

  • 使用 QEMU 的全系统模拟:模拟 MMU、设备等完整硬件,客户机操作系统无需修改即可运行;
  • 支持 30+ 处理器架构:包括 x86_64、ARM64 和 RISC-V 等;
  • VGA 图形模式:基于 SPICE 与 QXL 显示设备;
  • 文本终端模式:适用于无需图形界面的场景;
  • USB 设备支持:可将宿主机 USB 设备直通/转发给客户机;
  • 基于 QEMU TCG 的 JIT 加速:通过动态代码生成提升模拟性能;
  • 现代化前端:采用最新 API,为 macOS 11+ 和 iOS 11+ 从头设计;
  • 设备端全流程管理:可直接在设备上创建、管理、运行虚拟机。

上述"30+ 处理器"能力可在 Configuration 目录下的QEMUConstantGenerated.swift等文件中得到印证——UTM 将大量 QEMU 常量(架构、机型、网卡、声卡等)以枚举形式集中定义,其中架构、机型等枚举即对应 QEMU 支持的处理器类型。这些常量由 scripts/const-gen.py 生成,且 QEMUConstant.swift 中明确了QEMUConstant协议"可以由外部生成"(A QEMU constant is a enum that can be generated externally),体现了 UTM 通过脚本化方式与上游 QEMU 保持同步的设计思路。

macOS 上的额外能力

在 macOS 平台,UTM 相比 iOS 多出两类关键能力:

  • 硬件加速虚拟化:使用 macOS 自带的Hypervisor.framework配合 QEMU,实现同架构虚拟化(x86 虚拟机运行于 Intel Mac,ARM64 虚拟机运行于 Apple Silicon Mac)。由于该框架在 iOS 上不可用,这是 macOS 的独占特性(详见 Architecture.md);
  • macOS 客户机支持:在 macOS 12+ 上使用 Apple 的Virtualization.framework直接启动 macOS 客户机。此时后端不再是 QEMU,而是苹果原生的虚拟化框架,对应仓库中的 UTMAppleVirtualMachine.swift 与 UTMAppleConfiguration.swift。

二、UTM SE:无 JIT 的"较慢版"实现原理

UTM SE(SE 即 "Slower Edition" / 较慢版)是项目的一个重要分支,其存在源于 iOS 平台的 JIT 限制。

为什么需要 UTM SE

UTM/QEMU 需要动态代码生成(JIT)才能获得最佳性能。但在 iOS 上启用 JIT 需要越狱设备,或者利用针对特定 iOS 版本的各种变通方法(如下文介绍的 Tethered Launch 调试器启动方案)。对于无法越狱、也没有变通条件的普通用户,JIT 是一个硬门槛。

线程解释器(TCI)方案

UTM SE 使用**线程解释器(Threaded Code Interpreter,TCI)**来替代 JIT。TCI 的性能优于传统解释器,但仍然比 JIT 慢,因此被命名为"较慢版"。这种技术与 iSH(README"相关项目"中列出的 iOS 用户态 Linux 终端模拟器)用于动态执行的技术相似。UTM SE 不需要越狱,也不需要任何 JIT 变通方法,可以作为常规应用程序直接侧载(sideload)

TCI 后端由上游 QEMU 社区成员 ktemkin 的 ARM64 TCTI 分支演进而来(见 Architecture.md 中关于 QEMU fork 特性的描述"ARM64 TCTI from @ktemkin (JIT-less iOS support)")。仓库中 patches/sources 列出的依赖清单也表明,UTM 使用自维护的utmapp/qemufork(版本为qemu-10.0.2-utm),并在 patches/data/qemu-10.0.2-utm/qemu-10.0.2-utm.patch 中携带了对该 fork 的定制补丁。

UTM SE 的架构裁剪

为了优化体积和构建时间,UTM SE 仅包含以下架构:ARM、PPC、RISC-V 和 x86(各有 32 位与 64 位变体)。这与完整版支持的 30+ 架构形成对比,属于明显的功能取舍。

在构建层面,UTM SE 对应独立的 Xcode Scheme:从 build_utm.sh 的用法说明可见可用 Scheme 为[iOS|iOS-TCI|iOS-Remote|macOS],其中iOS-TCI(TCI = Threaded Code Interpreter)即 UTM SE的构建目标;配合 iOSDevelopment.md 中"将iOS替换为iOS-SE即可构建 UTM SE"的说明,可以确认 SE 版在依赖 Sysroot(ios-tci-arm64)与编译目标上均与完整版相互独立。

三、整体架构:从后端引擎到 SwiftUI 前端

Architecture.md 给出了 UTM 的分层架构图,从上到下依次为:

┌────────────────────┬──────────────────────┐ │ iOS VM Display │ macOS VM Display │ ├────────────────────┴──────────────────────┤ │ SwiftUI │ ├───────────────────────────────────────────┤ │ UTMVirtualMachine │ ├────────────────┬──────────────────────────┤ │ CocoaSpice │ │ ├────────────────┤ Virtualization.framework │ │ QEMU (TCG/HVF) │ │ └────────────────┴──────────────────────────┘

3.1 QEMU:引擎层

QEMU 是 UTM 的骨干。UTM 运行的是自维护的 fork,针对 Darwin 平台做了多项优化(见 Architecture.md 与 patches/sources):

  • 将 QEMU 构建为共享库(而非独立可执行文件);
  • 为越狱 iOS 提供 APRR 支持;
  • 集成 @ktemkin 的ARM64 TCTI,实现无 JIT 的 iOS 支持(即 UTM SE 的基础);
  • 提供SPICE ANGLE 后端,实现硬件 GL 加速。

其中hvf加速器(Hypervisor.framework)提供 macOS 上的同架构虚拟化(x86→x86 或 ARM64→ARM64),且仅限 macOS。

3.2 UTMQemu:进程模型与沙盒引导

由于 iOS 不允许fork、直接使用 XPC 或启动新进程,UTM 在 iOS 上把 QEMU 主循环放进一个 pthread 中运行(代价是无法同时启动多个 QEMU 实例、且 QEMU 退出后不能重新拉起);而在 macOS 上则通过 XPC 将 QEMU 放入独立进程。这一层由UTMQemu统一管理,其 Objective-C 实现即 UTMQemuSystem.m——可以看到它通过函数指针qemu_init/qemu_main_loop/qemu_cleanup在 pthread 内执行 QEMU 主循环。

macOS 受 App Sandbox 限制,需要额外的"引导"代码:QEMUHelper(见 QEMUHelper.m)是拥有独立沙盒的 XPC 辅助进程,负责在沙盒内孵化QEMULauncher(见 QEMULauncher/main.c),再由 Launcher 真正运行 QEMU。这种"UTM 主程序 → XPC Helper → Launcher"的三层结构既满足了 App Sandbox 的单 Bundle ID 要求,也通过进程隔离提升了安全性。跨沙盒传递文件权限时,UTM 采用"主程序用NSOpenPanel获取标准 bookmark → 传给 XPC 进程 → XPC 进程转成 security scoped bookmark 回传主程序存储"的三步方案(详见 Architecture.md),而 SPICE 通信所用的 Unix socket 则存放在共享 App Group 目录中。

3.3 配置体系:UTMConfiguration

VM 配置以PLIST 格式存储,反序列化后映射为两种结构之一:

  • UTMQemuConfiguration(QEMU 后端):以Codable接口承载配置数据。从 UTMQemuConfiguration.swift 可见其内部划分为 Information(基本信息与图标)、System(系统)、QEMU(附加 QEMU 参数)、Input(输入)、Sharing(共享)、Display(显示)、Drive(磁盘)、Network(网络)、Serial(串口)、Sound(声音)等子结构,并通过CodingKeys与 PLIST 键一一对应;该文件还包含backend == .qemu的校验与isLegacy标记,支持从旧版(Legacy)配置自动迁移(Configuration/Legacy目录即存放迁移代码);
  • UTMAppleConfiguration(Virtualization.framework 后端,见 UTMAppleConfiguration.swift):纯 Swift 实现,同样基于Codable,但比NSDictionary支撑的 QEMU 配置更易扩展复杂数据结构。

UTMQemuSystem(Services/UTMQemuSystem.m)负责把UTMQemuConfiguration翻译成启动 QEMU 所需的命令行参数与环境变量;UTMQemuManager则在 VM 启动后通过QMP 协议(JSON over socket)提供运行时服务,包括停止/暂停/恢复、快照、鼠标与平板模式切换、挂载可移动磁盘镜像等。QMP 协议由 QEMU 的 QAPI schema 定义,UTM 基于 QEMU 的qapi-gen.py改造出参数生成器与面向NSDictionary的 QAPI C 访问器,从而透明地复用 QEMU 的全部命令、结构与事件。

3.4 图形栈:CocoaSpice 与 Metal

UTM 选择 SPICE 作为 QEMU 的前端协议,因为它相比 VNC 在 USB 转发、多显示器、客户机剪贴板共享(SPICE agent)与动态分辨率调整上能力更强。CocoaSpice,由UTMQemuVirtualMachine调用以控制 SPICE 客户端并响应客户端事件。

3.5 前端:SwiftUI + UIKit/AppKit

前端绝大部分使用 SwiftUI 2.0 构建,这决定了最低支持系统为 iOS 14 / macOS 11,也是项目无意回移植到更早系统的原因(Architecture.md 明确说明)。Platform/UTMData.swift 作为ObservableObject是主页面的"单一数据源",保存 VM 列表并提供创建、修改、移动等操作。VM 显示层因 SwiftUI 尚不足以承载全部交互需求而使用 UIKit(iOS)/AppKit(macOS)实现——例如 iOS 上用于模拟标准键盘缺失按键的自定义键盘附件视图(NIB 实现,见 Platform/iOS/Display 目录)。

四、安装方式

  • iOS 版 UTM / UTM SE:官方安装指引位于 getutm.app 的 install 页面(README 中提供了该入口);
  • macOS 版 UTM:官方站点 mac.getutm.app 提供下载。

需要说明的是,完整版 UTM 在 iOS 上依赖 JIT,普通侧载无法直接获得最佳性能;UTM SE 则无此限制,可直接作为常规 App 侧载(详见上文"UTM SE"一节)。

五、从源码构建:开发环境与构建脚本

README 将开发文档链接至 Documentation/MacDevelopment.md 与 Documentation/iOSDevelopment.md,本节提炼两篇文档的关键步骤,并结合仓库脚本给出可直接执行的命令。

5.1 获取源码

需递归克隆以拉取全部子模块:

git clone --recursive <UTM 仓库地址>

若已普通克隆,可事后补齐子模块:

git submodule update --init --recursive

5.2 依赖获取:预编译 Sysroot(推荐)

依赖分为"预编译"与"自编译"两条路径。预编译路径从 GitHub Actions 产物中下载对应平台的Sysroot-*归档并解压到仓库根目录(需要登录 GitHub 才能下载 artifacts)。macOS 只需下载本机架构的 Sysroot 即可本地运行;iOS 侧可按目标平台选择,例如ios-arm64(完整版)、ios-tci-arm64(UTM SE)、ios_simulator-x86_64/ios_simulator-arm64(模拟器)、visionos-arm64(visionOS)等(完整对应表见 iOSDevelopment.md)。

5.3 自编译依赖(高级)

自编译强烈建议在全新的 macOS 虚拟机中进行——部分依赖会无视架构地写入/usr/local/lib,宿主机上已装的libusbgawkcmake等包会破坏构建。步骤概览:

  1. 安装 Xcode Command Line Tools 与 Homebrew;
  2. 安装构建前置依赖:
    brew install bison pkg-config gettext glib-utils libgpg-error nasm meson pip3 install six pyparsing

    并确保bison$PATH中:

    export PATH=/usr/local/opt/bison/bin:/opt/homebrew/opt/bison/bin:$PATH
  3. 运行依赖构建脚本(macOS 用-p macos;iOS 用-p PLATFORM,如ios_simulator-tci):
    ./scripts/build_dependencies.sh -p macos -a arm64

    其中-a可为arm64x86_64;要构建通用二进制需分别跑两个架构后再执行./scripts/pack_dependencies.sh . macos arm64 x86_64合并。若正在开发 QEMU 并希望传入自定义 QEMU 源码路径,可使用-q PATH_TO_QEMU_SOURCE选项(注意必须使用 UTM 兼容的 QEMU fork)。

依赖清单可参考 patches/sources:除 QEMU(qemu-10.0.2-utm)外,还包含 SPICE 服务端(spice-0.14.3)与客户端(spice-gtk-0.42)、GLib、Pixman、OpenSSL、libtpms/swtpm(可信平台模块)、libusb、libslirp(用户态网络栈)、GStreamer 插件以及 GPU 加速相关的 ANGLE、libepoxy、MoltenVK、Mesa、virglrenderer、Vulkan-Loader 等,每个组件均带对应补丁文件(patches 目录)。

5.4 构建 UTM

命令行构建统一使用 scripts/build_utm.sh,其参数为:

./scripts/build_utm.sh -t TEAMID -k SDK -s SCHEME -a ARCH -o /path/to/output
  • -t:Team ID(iOS 可选、macOS 必需,用于 App Group);
  • -k:目标 SDK,如iphoneosiphonesimulatorxrosmacosx
  • -s:Scheme,可选iOSiOS-TCI(UTM SE)、iOS-RemotemacOS
  • -a:架构,arm64x86_64,macOS 通用二进制可传"arm64 x86_64"(需加引号);
  • -o:输出目录。

例如 macOS 版:

./scripts/build_utm.sh -t TEAMID -k macosx -s macOS -a arm64 -o /path/to/output/directory

iOS 完整版:

./scripts/build_utm.sh -k iphoneos -s iOS -a arm64 -o /path/to/output/directory

产物为未签名的.xcarchive。脚本内部调用xcodebuild archive,并对 iOS 的 Frameworks 执行lipo -thin瘦身(以规避 iOS 15 以下崩溃并节省磁盘),对 macOS 产物则直接注入macOS.entitlementsQEMUHelper.entitlementsQEMULauncher.entitlementsutmctl.entitlements做临时签名(详见 build_utm.sh 末尾的 codesign 段)。

5.5 Xcode 图形化开发

CodeSigning.xcconfig.sample复制为CodeSigning.xcconfig并填写相应值:

  • macOS:若拥有带 Hypervisor entitlements 的开发者账号,设置DEVELOPER_ACCOUNT_VM_ACCESS = YES;默认 Xcode 会构建缺少 USB 与网络桥接特性的未签名版本;
  • iOS:用DEVELOPMENT_TEAM替换你的 Team ID,用已注册的 Bundle ID 前缀替换PRODUCT_BUNDLE_PREFIX;付费账号可设DEVELOPER_ACCOUNT_PAID = YES以自动申请更大的内存限制 entitlement。

另外注意 macOS 上有个已知问题:带调试器附加启动 VM 可能崩溃,变通方法是先不附加调试器启动 UTM,启动 VM 后再通过 Debug → Attach to Process 附加。

5.6 打包与签名

build_utm.sh产出的.xcarchive(含 GitHub Actions 产物)必须重新签名后方可使用。

macOS(scripts/package_mac.sh)

  • 未签名包(缺少 USB 与网络桥接等特性):
    ./scripts/package_mac.sh unsigned /path/to/UTM.xcarchive /path/to/output

    生成可安装到/ApplicationsUTM.dmg

  • 签名包(Developer ID):
    ./scripts/package_mac.sh developer-id /path/to/UTM.xcarchive /path/to/output TEAM_ID PROFILE_UUID HELPER_PROFILE_UUID LAUNCHER_PROFILE_UUID

    需要注册开发者账号、Developer ID Application 证书,以及 UTM、QEMUHelper、QEMULauncher 三个带 Hypervisor entitlements(需向 Apple 单独申请并获批)的 provisioning profile;签名后可再向 Apple 申请公证(notarization);

  • Mac App Store 包:./scripts/package_mac.sh app-store ...,生成提交用UTM.pkg,需要 Apple Distribution 与 Mac App Distribution 两类证书。

iOS(scripts/package.sh)

  • 签名 IPA(需 Development 签名证书而非 Distribution,因为 UTM 需要get-task-allowentitlement,Apple 仅在 Development 签名下授予):
    ./scripts/package.sh signedipa /path/to/UTM.xcarchive /path/to/output TEAM_ID PROFILE_UUID
  • 未签名 IPA(可用 AltStore 或越狱设备 + AppSync Unified 安装):
    ./scripts/package.sh ipa /path/to/UTM.xcarchive /path/to/output
  • DEB 包(供 Cydia/Sileo + AppSync Unified 安装,内部包裹未签名 IPA):
    ./scripts/package.sh deb /path/to/UTM.xcarchive /path/to/output

免费 Apple 账号签名的 IPA 有7 天有效期,需每 7 天重新签名;越狱设备则可生成 fake-signed 的 DEB。

5.7 Tethered Launch:iOS 上启用 JIT 的调试器启动方案

iOS 14 起,Apple 封堵了此前获取 JIT 的漏洞,非越狱设备上最佳变通方案是"通过调试器启动"(Tethered Launch),完整步骤见 Documentation/TetheredLaunch.md(README 未直接链接,但该文档与 iOS 开发、UTM SE 背景强相关)。核心流程:

  1. 准备 Xcode、最新 IPA Release、iOS App Signer、Homebrew 与ios-deploybrew install ios-deploy);
  2. 用 iOS App Signer 对 IPA 重签名并导出,将UTM-signed.ipa改名.zip解压出Payload/
  3. 部署:
    ios-deploy --bundle /path/to/Payload/UTM.app
  4. 之后每次启动(不能从主屏幕启动):
    ios-deploy --justlaunch --noinstall --bundle /path/to/Payload/UTM.app

常见问题:若报"invalid code signature / inadequate entitlements / profile has not been explicitly trusted",需在 设置 → 通用 → 设备管理 中信任开发者描述文件;若报"Failed to register bundle identifier",需更换 Bundle Identifier 重试。也可在 Xcode 的 Window → Devices and Simulators 中勾选"Connect via network",实现无 USB 线缆的部署与启动。

六、相关项目与许可

相关生态项目

README"相关项目"一节列出了两个生态关联项目:iSH——在 iOS 上模拟用户态 Linux 终端、运行 x86 Linux 应用(其动态执行技术与 UTM SE 的 TCI 方案同源);a-shell——为 iOS 原生构建的通用 Unix 命令与工具集,通过终端接口访问。

许可说明

UTM 本身在宽容的 Apache 2.0 许可证下分发,但使用了若干 (L)GPL 组件:其中大多数为动态链接,但gstreamer 插件为静态链接,且部分代码取自 QEMU(GPL),因此若要重新分发此应用需特别注意许可证合规问题(详见 LICENSE)。

前端还依赖以下 MIT/BSD 许可的组件:IQKeyboardManager(键盘管理)、SwiftTerm(终端模拟)、ZIP Foundation(ZIP 读写)、InAppSettingsKit(应用内设置界面)。部分图标由 Freepik 从 flaticon.com 制作;持续集成(CI)由 MacStadium 开源计划托管。

七、结语

UTM 的价值在于把 QEMU 的强大模拟能力完整地搬进了 iOS 与 macOS 的沙盒生态:通过自维护的 QEMU fork(共享库化、APRR、TCTI、SPICE ANGLE)、pthread/XPC 双进程模型、PLIST + Codable 配置体系、SPICE/Metal 图形链路以及 SwiftUI 前端,实现了"设备上创建、管理、运行虚拟机"的完整体验;UTM SE 则以线程解释器换取了无越狱、无 JIT 变通的可侧载方案。对于开发者而言,MacDevelopment.md、iOSDevelopment.md、TetheredLaunch.md 与 scripts 下的构建/打包脚本构成了一条完整的"依赖 → 构建 → 签名 → 分发"链路,可据此自行编译、签名与部署 UTM 及其 SE 变体。

【免费下载链接】UTMVirtual machines for iOS and macOS项目地址: https://gitcode.com/gh_mirrors/ut/UTM

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询