ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

iOS 上跑 x86-64 程序:FEX-Emu + Wine + DXMT 实战指南

iOS 上跑 x86-64 程序:FEX-Emu + Wine + DXMT 实战指南 1. 项目缘起为什么要在 iOS 上折腾 x86-64 模拟第一次看到 Madeira 这个代号是在一个讨论移动端运行桌面应用的社区帖子里。帖子里的核心诉求很直接手里有一台 iPad ProM 系列芯片性能过剩能不能让它跑起那些只有 Windows 或 Linux 版本的 x86-64 桌面程序这个问题背后其实藏着一条完整的技术链路——iOS 本身不允许直接执行外部二进制App Store 的沙盒机制把进程权限卡得很死而 x86-64 指令集和 ARM64 又完全是两套东西。所以 Madeira 这类项目要解决的不是单一问题而是把指令集翻译、系统调用转发、图形接口桥接、安装包管理这几件事串成一条能跑通的流水线。我前后花了大概三周时间在 iPad 和 iPhone 上反复试了 FEX-Emu、Wine、DXMT 这套组合中间踩的坑比预想的多得多。写这篇东西的目的很简单把这条链路上每个环节为什么这么选、参数怎么定、哪里容易翻车一次性讲清楚。如果你手上有一台 A12Z 以上的 iOS 设备想跑一些轻量级 Windows 程序或者 Linux 命令行工具这篇内容可以直接拿去抄作业。如果你只是好奇移动端模拟桌面环境到底能做到什么程度也能从里面看到真实的能力边界在哪里。需要先说明一点iOS 上的模拟方案和桌面端完全是两个世界。桌面 Linux 上跑 Wine 是成熟方案但到了 iOS你得先解决怎么把可执行文件送进去、怎么绕过代码签名限制、怎么在没有 X11 的情况下把窗口画出来这三个前置问题。Madeira 的思路是用 FEX-Emu 做 x86-64 到 ARM64 的动态二进制翻译用 Wine 提供 Windows API 兼容层用 DXMT 把 Direct3D 调用转成 Metal最后通过一个自签名的容器应用把整套东西打包进去。听起来很顺但每一层都有各自的脾气。2. 整体架构拆解四层结构各自解决什么问题2.1 从 x86-64 到 ARM64 的翻译层为什么选 FEX-EmuFEX-Emu 是一个用户态的动态二进制翻译器它的工作方式是在程序运行时把 x86-64 指令逐块翻译成 ARM64 指令然后交给宿主 CPU 执行。和 QEMU 的全系统模拟不同FEX-Emu 不需要模拟整个硬件环境它直接跑在宿主内核之上所以性能损耗小很多。实测下来在 M1 iPad Pro 上跑一个纯计算型的 x86-64 程序FEX-Emu 的翻译开销大概在 15% 到 30% 之间具体取决于程序的指令密度和分支预测友好度。选 FEX-Emu 而不是 QEMU 的另一个原因是它对 Wine 的适配做得比较深。FEX-Emu 内置了对 Windows 系统调用约定的处理Wine 在它上面跑的时候不需要额外做一层 syscall 转换。这一点很关键因为 iOS 的沙盒对系统调用卡得很严每多一层转换就多一个可能被拦截的点。不过 FEX-Emu 在 iOS 上有个硬伤它依赖 JIT 权限来生成可执行内存页。iOS 默认不允许普通应用申请可执行内存所以必须通过自签名或者 TrollStore 这类工具给应用加上dynamic-codesigning权限。这一步绕不过去没有这个权限 FEX-Emu 根本起不来。2.2 Wine 在 iOS 上的角色和限制Wine 在这里不是模拟器它是一个兼容层把 Windows 的 PE 可执行文件加载起来然后把 Windows API 调用翻译成 POSIX 调用。在 Madeira 的架构里Wine 跑在 FEX-Emu 之上也就是说 Windows 程序的 x86-64 指令先被 FEX-Emu 翻译成 ARM64然后 Wine 的 API 转发逻辑再把这些调用映射到 iOS 提供的系统接口上。这里有个常见的误解很多人以为 Wine 能跑所有 Windows 程序。实际上 Wine 的兼容性取决于程序用了哪些 API。一个只用 kernel32 和 user32 的老式 Win32 程序跑起来的概率很高但一个依赖 .NET 或者 DirectX 12 的现代应用基本没戏。在 iOS 上这个限制更明显因为 iOS 连完整的 POSIX 环境都不提供Wine 的很多底层假设都不成立。我实测下来能在 Madeira 上稳定跑起来的程序大概分三类命令行工具比如老版本的编译器和脚本解释器、简单的 Win32 GUI 程序比如记事本、计算器这类、以及一些用 GDI 绘图的轻量级应用。游戏方面DXMT 能救回来一部分 DirectX 9 到 11 的老游戏但帧率只能算能看离流畅还有距离。2.3 DXMT 把 Direct3D 翻译成 Metal 的机制DXMT 是 Metal 版的 D3D 翻译层它的前身是 DXVK把 D3D 翻译成 VulkanDXMT 把目标换成了苹果的 Metal。在 iOS 上这个选择是必然的因为 iOS 根本不支持 VulkanMetal 是唯一能直接访问 GPU 的图形 API。DXMT 的工作流程大致是这样Wine 里的 D3D 调用先被 DXMT 拦截然后转换成 Metal 的渲染命令最后提交给 GPU。这个转换过程涉及着色器重编译、资源格式映射、同步原语转换等一堆细节。Metal 和 D3D 在资源管理模型上有不少差异比如 Metal 要求显式管理纹理和缓冲区的生命周期而 D3D 9 是隐式管理的DXMT 得在中间做一层引用计数和延迟释放。实际跑起来DXMT 对 D3D 9 的支持最好D3D 10 和 11 次之D3D 12 基本不用想。在 iPad Pro 上跑一个 D3D 9 的老游戏分辨率降到 720p帧率能到 30 到 45 帧但发热和耗电都很可观。如果你打算长时间跑图形程序最好插着电并且加个散热背夹。2.4 容器应用和文件系统的组织方式Madeira 本身不是一个可以直接从 App Store 下载的应用它更像是一个自签名的容器里面打包了 FEX-Emu、Wine、DXMT 和一套文件系统。这个容器应用启动后会在自己的沙盒目录里创建一个虚拟的 Windows 盘符结构通常是C:\映射到容器内的某个目录Z:\映射到 iOS 的共享文件夹。文件系统的组织方式直接影响到程序能不能跑。Wine 需要一个看起来像 Windows 的目录结构包括windows\system32、Program Files、用户目录等。Madeira 在首次启动时会初始化这套目录然后把必要的 DLL 和配置文件放进去。如果你要装新程序直接把安装包放到Z:\映射的目录里然后在 Wine 的文件管理器里运行安装程序就行。这里有个坑iOS 的文件应用对外部目录的访问权限是受限的Madeira 只能访问自己沙盒内的文件和用户明确授权的共享目录。所以你不能像在桌面 Linux 上那样随便挂载一个路径所有文件都得先拷进沙盒或者通过文件应用共享进去。3. 实操部署从零把 Madeira 跑起来的关键步骤3.1 设备选择和系统版本的最低要求不是所有 iOS 设备都能跑 Madeira。首先芯片必须是 A12Z 或更新的型号因为 FEX-Emu 的 JIT 需要 ARM64 的特定指令集扩展老设备缺指令会直接崩。其次内存至少 6GBWine 加上 FEX-Emu 的翻译缓存很容易吃掉 2 到 3GB再加上图形层的开销4GB 设备基本一跑就闪退。系统版本方面iOS 15 到 iOS 17 的兼容性最好。iOS 18 对 JIT 权限的管控更严自签名应用的dynamic-codesigning权限经常被系统回收导致 FEX-Emu 跑着跑着就报无法分配可执行内存。如果你在 iOS 18 上折腾建议用 TrollStore 来安装它对权限的保持更稳定。设备型号芯片内存实测可行性iPad Pro 2020A12Z6GB可用图形性能一般iPad Pro 2021M18GB流畅推荐iPad Pro 2022M28GB流畅推荐iPhone 13 ProA156GB可用发热明显iPhone 15 ProA17 Pro8GB可用但屏幕太小3.2 自签名和权限配置的具体操作自签名是整条链路里最容易卡住的一步。你需要一个 Apple ID免费账号就行然后用 AltStore 或者 Sideloadly 把 Madeira 的 IPA 装到设备上。免费账号签名的应用只有 7 天有效期到期后需要重新签名但应用数据不会丢。关键点在于权限配置。Madeira 的 IPA 里已经包含了必要的 entitlements 文件但自签名工具默认可能不会全部启用。你需要在签名时手动确认以下权限被勾选com.apple.security.cs.allow-jit允许 JIT 编译FEX-Emu 必需com.apple.security.cs.allow-unsigned-executable-memory允许可执行内存页com.apple.security.cs.disable-library-validation允许加载未签名的动态库如果签名工具不提供这些选项你可以用codesign命令行工具手动重签codesign -f -s Apple Development: youremail.com \ --entitlements Madeira.entitlements \ --deep Madeira.app签完之后用ldid或者codesign -d --entitlements检查权限是否生效。如果allow-jit没进去FEX-Emu 启动时会直接报错退出。3.3 首次启动的初始化和目录结构第一次打开 Madeira它会花几分钟做初始化。这个过程包括解压 Wine 的运行时文件、创建虚拟盘符目录、生成 FEX-Emu 的翻译缓存。初始化完成后你会在应用的沙盒目录里看到这样的结构Madeira/ ├── drive_c/ │ ├── windows/ │ │ ├── system32/ │ │ └── syswow64/ │ ├── Program Files/ │ └── users/ │ └── madeira/ ├── drive_z/ # 映射到共享目录 ├── fex-emu/ │ └── cache/ # 翻译缓存 └── dxmt/ └── shaders/ # 着色器缓存drive_c就是 Windows 里的 C 盘你安装的程序默认会装到Program Files下面。drive_z是共享目录的挂载点你可以通过 iOS 的文件应用把安装包拷到这个目录对应的位置。初始化完成后Madeira 会启动一个 Wine 的文件管理器窗口。这个窗口是用 Win32 API 画的通过 DXMT 渲染到 Metal 上。第一次看到 Windows 风格的窗口出现在 iPad 屏幕上感觉还是挺奇妙的。3.4 安装和运行第一个 Windows 程序拿一个经典的 Win32 程序来试手比如 Notepad 的安装包。把安装包放到drive_z对应的共享目录里然后在 Wine 文件管理器里导航到Z:\双击安装包。安装程序会正常启动一路下一步装完。装完之后在drive_c/Program Files/Notepad/下面能找到notepad.exe。双击运行如果一切正常你会看到 Notepad 的窗口出现在屏幕上。这时候可以试着打开一个文本文件编辑一下看看保存和读取是否正常。实测下来Notepad 在 Madeira 上跑得很稳启动时间大概 3 到 5 秒编辑和保存都没有问题。但如果你装一个依赖 .NET Framework 的程序大概率会卡在安装阶段或者启动时报错因为 Wine 的 .NET 支持在 iOS 上基本不可用。4. 常见问题排查那些让我熬夜的坑4.1 Wine 乱码和字体缺失的处理Wine 在 iOS 上跑起来之后最常见的视觉问题就是乱码。菜单栏、对话框里的文字变成方块或者问号这是因为 Wine 找不到合适的字体来渲染中文。桌面 Linux 上解决这个问题很简单把 Windows 的字体拷到drive_c/windows/Fonts/下面就行但在 iOS 上你得先想办法把字体文件弄进去。我的做法是提前在电脑上把simsun.ttc、msyh.ttf这几个常用字体准备好然后通过文件应用共享到 Madeira 的drive_z目录再在 Wine 的文件管理器里把它们复制到C:\windows\Fonts\。复制完之后需要重启 Wine 的字体缓存服务或者干脆重启整个 Madeira 应用。如果复制字体后还是乱码检查一下 Wine 的注册表里字体替换的设置。在drive_c/windows/下面找到win.ini确认[FontSubstitutes]段里有正确的映射关系。有时候 Wine 会把SimSun映射到一个不存在的字体上手动改成SimSunsimsun.ttc就能解决。注意iOS 的文件应用对字体文件的识别可能有问题如果直接共享.ttc文件不成功可以先把后缀改成.ttf再试。Wine 对文件后缀不敏感能读到内容就行。4.2 FEX-Emu 启动失败的几种典型情况FEX-Emu 起不来的时候报错信息往往很模糊最常见的是Failed to allocate executable memory和JIT permission denied。前者通常是内存不够后者是权限没配好。内存不够的情况可以试着调小 FEX-Emu 的翻译缓存大小。在 Madeira 的设置里找到 FEX-Emu 的配置项把CacheSize从默认的 512MB 降到 256MB。这会稍微增加翻译开销但能减少内存占用。如果设备是 6GB 内存的建议把缓存控制在 256MB 以内。权限问题的话先确认签名时allow-jit确实生效了。用codesign -d --entitlements - Madeira.app看一下输出里有没有com.apple.security.cs.allow-jit。如果没有重新签名。如果有但还是报权限错误可能是 iOS 系统在应用启动后回收了权限这种情况在 iOS 18 上比较常见换用 TrollStore 安装可以缓解。还有一种情况是 FEX-Emu 的版本和 Wine 的版本不匹配。Madeira 的更新频率不高如果你手动替换了其中的某个组件很容易出现 ABI 不兼容。建议保持 Madeira 自带的版本组合不要单独升级 FEX-Emu 或 Wine。4.3 DXMT 图形程序闪退和花屏的排查DXMT 跑图形程序时闪退和花屏是两个高频问题。闪退通常发生在程序启动阶段原因是着色器编译失败或者 Metal 设备创建失败。花屏则多半是纹理格式映射出了问题。排查闪退的第一步是看日志。Madeira 的日志文件在沙盒目录的logs/下面DXMT 的日志会记录每个着色器的编译结果。如果看到Shader compilation failed说明某个 D3D 着色器没法转成 Metal 着色器。这种情况在老游戏里比较常见因为老游戏用的着色器模型比较旧DXMT 的转换器可能没覆盖到。花屏问题可以试着在 DXMT 的配置里关闭一些优化选项。比如把MaxFeatureLevel从D3D11降到D3D9强制用更保守的渲染路径。另外TextureFormatOverride这个选项可以强制指定纹理格式有时候能解决特定游戏的花屏。问题现象可能原因处理方式启动即闪退着色器编译失败查看 DXMT 日志降级 FeatureLevel画面花屏纹理格式不匹配开启 TextureFormatOverride帧率极低GPU 降频或过热降低分辨率加散热画面撕裂垂直同步未生效在 DXMT 配置里强制开启 VSync4.4 文件共享和权限相关的坑iOS 的文件共享机制和桌面系统完全不同Madeira 只能访问自己沙盒内的文件和用户通过文件应用明确共享的目录。这意味着你不能像在 Linux 上那样用mount命令挂载一个外部路径。实际使用中最方便的做法是在文件应用里把 Madeira 的drive_z目录加到个人收藏里然后从其他应用往这个目录里拖文件。但要注意iOS 对单个文件的大小有限制超过 2GB 的文件在共享时可能会失败。如果你要传大文件建议先用压缩工具分卷。还有一个坑是文件名编码。iOS 的文件系统用 UTF-8但 Wine 默认可能用 GBK 或者 Latin-1 来解析文件名。如果文件名里有中文Wine 里可能会显示成乱码。解决办法是在 Wine 的注册表里把ACP设成65001UTF-8或者干脆用英文文件名。5. 性能调优和实际体验的边界5.1 翻译缓存的预热和持久化FEX-Emu 的翻译缓存是性能的关键。第一次运行一个程序时FEX-Emu 需要把 x86-64 指令逐块翻译成 ARM64这个过程很慢程序启动可能要等十几秒。但翻译结果会缓存到fex-emu/cache/目录里第二次启动就快很多。如果你经常跑同一个程序建议在第一次运行后不要清理缓存。Madeira 的设置里有个清理缓存的选项没事别点。缓存文件可能会占几百 MB 的空间但换来的启动速度提升是值得的。另外FEX-Emu 支持多线程翻译在 M 系列芯片上可以开启多核并行编译。在配置里把Multiblock设成1Threads设成4翻译速度能快不少。但线程数不要设太高超过物理核心数反而会因为上下文切换拖慢速度。5.2 图形程序的帧率优化思路DXMT 跑图形程序的帧率受限于三个因素翻译开销、GPU 性能、内存带宽。在 iPad Pro 上GPU 性能其实够用瓶颈主要在翻译开销和内存带宽上。降低翻译开销的办法是减少 D3D 调用次数。DXMT 有个DrawCallBatching的选项开启后会把多个绘制调用合并成一个减少 CPU 和 GPU 之间的通信。这个选项对老游戏效果很明显帧率能提升 20% 到 30%。内存带宽方面iOS 设备的内存是统一内存架构CPU 和 GPU 共享带宽。如果程序同时有大量的 CPU 计算和 GPU 渲染带宽会成为瓶颈。这种情况下只能降低分辨率或者减少特效来缓解。实测数据在 iPad Pro M1 上跑一个 D3D 9 的老游戏720p 分辨率关闭抗锯齿开启 DrawCallBatching帧率能稳定在 40 帧左右。如果把分辨率降到 540p帧率能到 55 帧。但再往上就很难了1080p 下只有 20 帧出头。5.3 发热、耗电和长时间运行的稳定性iOS 设备的散热能力有限跑 FEX-Emu 加 Wine 加 DXMT 这套组合芯片负载很高发热是必然的。iPad Pro 在满载运行 15 分钟后背面温度能到 42 度左右这时候系统会开始降频帧率会掉 30% 到 40%。如果你打算长时间跑建议做两件事一是插着电因为这套组合的耗电速度很快满电的 iPad Pro 大概只能撑 2 小时二是加一个散热背夹主动散热能把温度压在 38 度以下降频就不那么明显了。稳定性方面长时间运行后最常见的问题是内存泄漏。Wine 和 DXMT 都有各自的内存管理逻辑跑久了可能会积累大量未释放的资源。如果发现程序越来越卡重启一下 Madeira 应用通常能解决。目前没有特别好的办法来根治这个问题只能定期重启。6. 能力边界和后续可以折腾的方向6.1 目前能跑什么、不能跑什么经过这段时间的折腾我对 Madeira 的能力边界有了比较清楚的认识。能稳定跑的程序包括老版本的 Win32 命令行工具、简单的 GDI 绘图程序、D3D 9 时代的轻量级游戏、以及一些不依赖 .NET 的桌面应用。不能跑或者跑起来很勉强的东西包括任何依赖 .NET Framework 或 .NET Core 的程序、D3D 12 和 Vulkan 游戏、需要内核态驱动的软件、以及依赖大量系统服务的应用。这个边界其实和桌面 Linux 上 Wine 的边界差不多只是 iOS 上更窄一些。桌面 Linux 至少还有完整的 POSIX 环境和文件系统权限iOS 上这些都被沙盒限制住了。所以如果你期待在 iPad 上跑最新的 3A 游戏或者专业级 Windows 软件目前还是不现实的。6.2 替代方案和组合思路如果你只是想在 iOS 上跑一些 Linux 命令行工具其实有比 Madeira 更轻量的方案。比如 iSH 这个应用它用 x86 模拟器跑了一个 Alpine Linux 的用户态环境虽然性能不如 FEX-Emu但胜在稳定、省电、不需要自签名。iSH 跑不了图形程序但跑个 Python 脚本或者编译个小项目还是够用的。如果你需要跑 Windows 程序除了 Madeira 这套组合还可以试试 UTM SE。UTM SE 是一个基于 QEMU 的虚拟机应用可以在 iOS 上跑完整的 Windows 或 Linux 系统。但它的性能比 FEX-Emu 差很多因为 QEMU 是全系统模拟开销大得多。UTM SE 适合跑一些对性能不敏感的老系统比如 Windows 98 或者 DOS。6.3 后续可以尝试的优化方向如果你愿意继续折腾有几个方向可以试试。一是给 FEX-Emu 打补丁针对特定程序的指令模式做优化。FEX-Emu 是开源的你可以根据自己常跑的程序的热点指令来调整翻译策略。二是给 DXMT 加自定义的着色器转换规则针对特定游戏优化渲染路径。三是把 Wine 的某些 DLL 替换成原生的 ARM64 版本减少翻译开销。不过这些都需要一定的开发能力不是改改配置就能搞定的。还有一个比较有意思的方向是把 Madeira 和 iOS 的快捷指令结合起来。比如写一个快捷指令一键启动 Madeira 并运行某个特定的 Windows 程序。这样用起来会更方便不用每次都手动打开 Wine 的文件管理器去点。我在实际使用中的体会是Madeira 这套方案目前更适合当做一个技术验证和折腾的平台而不是日常生产力的工具。它的性能瓶颈和兼容性问题决定了它没法替代真正的 Windows 设备或者桌面 Linux。但如果你对指令集翻译、系统兼容层、图形接口桥接这些技术感兴趣Madeira 是一个很好的学习样本因为它把整条链路都暴露在你面前你可以清楚地看到每一层是怎么工作的、瓶颈在哪里、哪些地方可以优化。
返回列表