ARTICLE DETAIL

资讯详情

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

ARM设备运行Windows程序:Wine+FEX-Emu+DXMT跨平台兼容实战

ARM设备运行Windows程序:Wine+FEX-Emu+DXMT跨平台兼容实战 1. 从“Madeira”说起一个跨平台兼容项目的整体设计思路“Madeira”这个名字乍一看像是个地名但在跨平台兼容圈子里它代表的是一个把 Windows 应用搬到非 Windows 环境里跑的项目方向。结合热搜词里的 Wine、FEX-Emu、DXMT、iOS、x86-64 这几个关键词基本可以判断出这个项目的核心目标在 ARM 架构的设备上尤其是移动端和嵌入式 Linux 设备上通过多层翻译与兼容技术让原本为 x86-64 Windows 编译的应用程序和游戏能够正常运行。我最早接触这类方案是在折腾 Linux 桌面跑 Windows 游戏的时候那时候 Wine 还是主力DXVK 刚起步能跑起来一个 DirectX 9 的游戏就谢天谢地了。后来 ARM 设备性能上来了尤其是 Apple Silicon 和各类 ARM 开发板普及之后x86-64 到 ARM64 的指令翻译需求变得非常迫切。FEX-Emu 就是在这个背景下进入视野的它专门做 x86-64 到 ARM64 的用户态指令翻译配合 Wine 使用就能在 ARM 设备上跑 Windows 程序。Madeira 这个项目要解决的问题很明确降低普通用户使用这套复杂技术栈的门槛。Wine 本身配置就够折腾了再加上 FEX-Emu 做指令翻译、DXMT 做 DirectX 到 Metal 的转换中间任何一环出问题都跑不起来。Madeira 的价值在于把这些组件打包整合提供相对统一的配置入口和运行环境。适合谁来参考这个项目三类人最值得看一是在 ARM Linux 设备上想跑 Windows 应用的用户比如用树莓派、RK3588 开发板或者国产 ARM 笔记本的人二是做 iOS 越狱环境或者侧载工具开发的工程师热搜词里 iOS 出现频率极高说明移动端也有类似需求三是对 Wine 兼容层技术本身感兴趣、想了解 x86-64 翻译原理的开发者。哪怕你只是好奇“为什么 Windows 程序能在别的系统上跑”这套东西拆开看也很有收获。2. 核心技术栈拆解Wine、FEX-Emu、DXMT 各自扮演什么角色2.1 Wine 到底做了什么为什么会有乱码问题Wine 不是模拟器它是一套兼容层实现了 Windows 的 API。你可以把它理解成一个“翻译官”Windows 程序调用CreateWindowWine 就把这个调用翻译成 Linux 的 X11 或者 Wayland 对应的操作。它不翻译 CPU 指令所以 Wine 本身只能跑和当前系统同架构的 Windows 程序。在 x86-64 Linux 上跑 x86-64 Windows 程序Wine 直接就能干活但在 ARM64 设备上Wine 就无能为力了因为指令集对不上。热搜词里“wine 乱码”和“wine 栏是乱码”出现多次这是 Wine 最经典的问题之一。乱码的根源通常有两个字体缺失和区域设置不匹配。Wine 默认使用它自带的字体库如果系统里没有安装对应的中文字体菜单栏和对话框就会显示成方块或者问号。另一个原因是LANG和LC_ALL环境变量没有正确设置Wine 不知道应该用哪种编码来渲染文本。解决乱码的实操步骤我试过很多次最稳的方案是# 安装中文字体 sudo apt install fonts-wqy-microhei fonts-wqy-zenhei # 把字体链接到 Wine 的字体目录 ln -s /usr/share/fonts/truetype/wqy/wqy-microhei.ttc ~/.wine/drive_c/windows/Fonts/ # 设置环境变量 export LANGzh_CN.UTF-8 export LC_ALLzh_CN.UTF-8如果还不行就得用wine regedit打开注册表把HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes里的MS Shell Dlg替换成WenQuanYi Micro Hei。这个操作我踩过坑改完注册表一定要重启 Wine 服务不然不生效。2.2 FEX-Emux86-64 到 ARM64 的指令翻译引擎FEX-Emu 是这个技术栈里最硬核的部分。它的工作是把 x86-64 的机器指令实时翻译成 ARM64 指令。你可以把它想象成一个“同声传译”x86-64 程序每执行一条指令FEX-Emu 就把它转换成等效的 ARM64 指令序列。这个过程有缓存机制翻译过的代码块会被存起来下次直接调用所以性能不会一直很差。FEX-Emu 的核心技术点在于寄存器映射和标志位处理。x86-64 有 16 个通用寄存器ARM64 有 31 个数量上够用但 x86-64 的EFLAGS寄存器在 ARM64 上没有直接对应物FEX-Emu 需要用额外的指令来模拟标志位的计算。这就是为什么翻译后的性能通常只有原生的 50% 到 70%具体取决于程序的指令特征。配置 FEX-Emu 的时候有几个关键参数需要注意参数作用推荐值FEX_TSOENABLED控制内存顺序模拟1默认开启FEX_MULTIBLOCK多块编译优化1FEX_ROOTFS指定根文件系统路径根据实际路径设置FEX_CORE指定 CPU 核心类型根据设备填写FEX_TSOENABLED这个参数特别重要。x86-64 是强内存模型ARM64 是弱内存模型如果不开启 TSO 模拟多线程程序很容易出现数据竞争导致崩溃。我实测下来跑单线程程序可以关掉 TSO 提升性能但多线程程序必须开着不然稳定性没法保证。2.3 DXMT把 DirectX 调用转成 MetalDXMT 是 DirectX 到 Metal 的翻译层主要用在 Apple 设备上。它的作用和 DXVK 类似DXVK 是把 DirectX 转成 VulkanDXMT 是转成 Metal。为什么需要这个因为 Wine 本身只实现了 DirectX 的 API 接口真正的图形渲染还是要靠底层图形 API 来完成。在 macOS 和 iOS 上Metal 是官方推荐的图形 API性能最好所以 DXMT 就成了首选。DXMT 目前对 DirectX 11 的支持比较完善DirectX 12 还在开发中。热搜词里“ios游戏”和“ios设备模拟”说明很多人想在 iOS 上跑 Windows 游戏这个技术路线是可行的但性能损耗比较大。Metal 和 DirectX 的渲染管线差异不小DXMT 需要做大量的状态转换和资源重绑定实际游戏帧率通常只有原生 Windows 的 30% 到 50%。3. 实操环境搭建从零开始跑通一个 Windows 程序3.1 基础环境准备与依赖安装在 ARM64 Linux 设备上搭建这套环境第一步是确认系统架构和内核版本。用uname -m查看输出应该是aarch64。内核版本建议 5.15 以上低版本内核在内存管理和调度上可能有问题。依赖安装这块不同发行版命令不一样我以 Ubuntu 22.04 ARM64 为例# 更新软件源 sudo apt update sudo apt upgrade -y # 安装基础依赖 sudo apt install -y build-essential cmake ninja-build git python3-pip \ libsdl2-dev libvulkan-dev libgl1-mesa-dev libgles2-mesa-dev \ libfontconfig1-dev libfreetype6-dev libx11-dev libxext-dev # 安装 Wine 的依赖 sudo apt install -y wine64 wine32 winetricks这里有个坑要注意ARM64 上的 Wine 包和 x86-64 上的不一样。Ubuntu 官方源里的wine64在 ARM64 上其实是配合 FEX-Emu 使用的版本它本身不包含 x86-64 的指令翻译能力。你需要确认安装的是wine64还是wine64-fex之类的变体包。如果源里没有就得从源码编译或者用第三方构建。FEX-Emu 的安装推荐用官方 PPA 或者从源码编译。源码编译的步骤大致如下git clone https://github.com/FEX-Emu/FEX.git cd FEX git submodule update --init --recursive mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease -DCMAKE_INSTALL_PREFIX/usr/local .. make -j$(nproc) sudo make install编译过程大概需要 20 到 40 分钟取决于设备性能。编译完成后用FEXInterpreter --version验证是否安装成功。3.2 Wine 前缀配置与 FEX-Emu 集成Wine 的前缀prefix是它模拟的 Windows 环境目录默认在~/.wine。在 ARM64 上配合 FEX-Emu 使用时需要创建一个专门的前缀并且指定用 FEX-Emu 来执行 x86-64 的 Windows 程序。# 创建新的 Wine 前缀 export WINEPREFIX~/madeira-wine export WINEARCHwin64 wineboot --init # 配置 FEX-Emu 作为执行后端 export FEX_ROOTFS/usr/local/share/fex-emu/rootfs export FEX_APP_CONFIG/usr/local/share/fex-emu/AppConfigFEX_ROOTFS指向的是 FEX-Emu 的根文件系统里面包含了 x86-64 的基础库和运行时。这个目录在安装 FEX-Emu 时应该已经自动创建了如果没有需要手动从 FEX-Emu 的发布包里解压。集成测试可以用一个简单的 Windows 程序来验证比如notepad.exeFEXInterpreter wine notepad.exe如果记事本窗口能正常弹出来说明 Wine 和 FEX-Emu 的集成基本没问题。如果报错常见原因是FEX_ROOTFS路径不对或者权限不足。我遇到过因为/usr/local/share/fex-emu目录权限是 root 所有普通用户读不了导致启动失败的情况用chmod -R 755改一下权限就好了。3.3 DXMT 的编译与配置DXMT 的编译需要 macOS 或者 iOS 的开发环境因为它依赖 Metal 框架。在 Linux 上没法直接编译 DXMT但可以用预编译的二进制包。如果你是在 macOS 上折腾步骤大致是git clone https://github.com/3Shain/dxmt.git cd dxmt mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease -DCMAKE_OSX_ARCHITECTURESarm64 .. make -j$(nproc)编译产物是dxmt.dll和相关的 Metal 着色器库。把这些文件放到 Wine 前缀的drive_c/windows/system32目录下然后在 Wine 的 DLL 覆盖设置里把d3d11和dxgi指向 DXMT 的实现。配置 DLL 覆盖可以用winecfg图形界面操作也可以直接改注册表wine reg add HKEY_CURRENT_USER\Software\Wine\DllOverrides /v d3d11 /t REG_SZ /d native /f wine reg add HKEY_CURRENT_USER\Software\Wine\DllOverrides /v dxgi /t REG_SZ /d native /f这里native表示使用 DXMT 提供的 DLL而不是 Wine 自带的实现。改完之后跑一个 DirectX 11 的程序测试如果画面正常渲染说明 DXMT 配置成功了。4. 常见问题与排查技巧实录4.1 Wine 乱码问题的完整排查流程乱码问题我遇到过不下十次每次原因都不太一样。整理了一个排查流程按顺序检查基本能覆盖 90% 的情况。第一步确认系统字体是否安装。用fc-list :langzh查看中文字体列表如果输出为空说明系统里没有中文字体需要先安装。第二步检查 Wine 的字体目录。ls ~/.wine/drive_c/windows/Fonts/看看里面有没有中文字体文件。如果没有把系统字体复制或者链接过去。第三步检查环境变量。echo $LANG和echo $LC_ALL确保输出是zh_CN.UTF-8或者en_US.UTF-8。如果是C或者POSIXWine 会用默认编码中文就会乱码。第四步检查注册表字体替换。用wine regedit打开注册表定位到HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes看看MS Shell Dlg和MS Shell Dlg 2的值是不是指向了支持中文的字体。第五步如果以上都正常但还是乱码可能是程序本身用了自定义字体渲染。这种情况比较麻烦需要在程序目录下放一个fontconfig配置文件强制替换字体。我遇到过一个老程序只认SimSun字体最后是把wqy-microhei.ttc重命名为simsun.ttc放到字体目录才解决的。问题现象可能原因解决方法菜单栏显示方块系统无中文字体安装fonts-wqy-microhei对话框文字问号环境变量未设置设置LANGzh_CN.UTF-8部分文字乱码字体替换未配置修改注册表 FontSubstitutes程序内文字乱码程序自带字体渲染放置 fontconfig 配置文件4.2 FEX-Emu 性能调优与崩溃排查FEX-Emu 跑游戏或者大型软件时性能调优很关键。默认配置下FEX-Emu 会开启所有兼容性选项稳定性最好但性能不是最优。如果追求帧率可以尝试关闭一些非必要的模拟。FEX_TSOENABLED0可以关闭强内存模型模拟单线程程序性能能提升 10% 到 20%。但多线程程序千万别关我试过在一个多线程视频转码程序上关掉 TSO结果跑了三分钟就崩溃了日志里全是内存访问冲突。FEX_MULTIBLOCK1开启多块编译能让 FEX-Emu 一次性翻译更大的代码块减少翻译开销。这个选项对大型程序效果明显但会增加内存占用。在内存紧张的设备上要权衡一下。崩溃排查方面FEX-Emu 的日志输出很有用。设置FEX_LOG_LEVELdebug可以看到详细的指令翻译记录。如果程序在某个特定操作后崩溃日志里通常能找到对应的 x86-64 指令和翻译后的 ARM64 指令对比一下就能发现是哪个指令的翻译出了问题。我遇到过一个典型问题某个程序用了 x86-64 的RDRAND指令生成随机数FEX-Emu 对这个指令的模拟有问题导致程序每次生成的随机数都一样。解决办法是在 FEX-Emu 配置里把RDRAND指令重定向到系统的随机数生成器。这个坑花了我两天才找到因为程序崩溃的现场和随机数完全没关系是随机数重复导致后续逻辑出错才崩的。4.3 DXMT 渲染问题与 iOS 环境特殊处理DXMT 在 iOS 上跑的时候有几个特殊问题需要注意。iOS 的 Metal 实现和 macOS 有差异某些 Metal 特性在 iOS 上不可用或者行为不同。比如MTLHeap在 iOS 上的内存管理策略更激进DXMT 如果按 macOS 的方式使用很容易被系统杀掉。热搜词里“ios开发者模式”和“ios 26.3.1怎么开发者模式”说明很多人卡在开发者模式开启这一步。iOS 16 以后开发者模式默认隐藏需要在“设置-隐私与安全性”里找到“开发者模式”开关打开后重启设备才生效。这个开关只有在设备连接过 Xcode 或者安装了开发者证书的应用后才会出现。“ios浏览器唤起安装app”和那个https://cb95f.advrbluks.com/download/jgdj/ios?aff_codeagskv链接从技术角度看是用了 iOS 的itms-services协议或者 Universal Link 来触发应用安装。这种方案在 iOS 上限制越来越多iOS 17 以后对非 App Store 安装的管控更严很多以前能用的方法现在都失效了。DXMT 在 iOS 上的部署需要越狱环境或者 TrollStore 这类永久签名工具。没有越狱的话只能通过 AltStore 或者 SideStore 侧载但每次重启后需要重新签名比较麻烦。我实测下来TrollStore 是最稳的方案安装一次永久有效不需要反复签名。渲染问题排查方面DXMT 的日志会输出 Metal 相关的错误信息。如果看到MTLCommandBufferError或者MTLTextureError通常是资源创建或者状态转换出了问题。可以尝试降低 DXMT 的渲染质量设置比如关闭 MSAA 或者降低纹理分辨率看是否能绕过问题。5. 跨平台兼容方案的选型对比与经验总结5.1 Wine FEX-Emu DXMT 与其他方案的对比在 ARM 设备上跑 Windows 程序除了 Wine FEX-Emu DXMT 这条路线还有几种方案可选。我整理了一个对比表格方便根据实际需求选择。方案原理优点缺点适用场景Wine FEX-Emu DXMT指令翻译 API 兼容 图形转换开源免费可定制性强配置复杂性能损耗大ARM Linux 设备跑 Windows 程序虚拟机方案完整硬件虚拟化兼容性最好资源占用高需要硬件支持性能足够的 ARM 服务器远程桌面在 x86 机器上运行串流画面性能接近原生依赖网络有延迟局域网内使用云游戏平台云端渲染客户端只负责显示客户端要求低需要付费依赖网络移动端玩大型游戏Wine FEX-Emu DXMT 这条路线最大的优势是完全本地运行不依赖网络也不需要额外的硬件。缺点也很明显配置门槛高性能损耗大不是所有程序都能跑起来。我个人的经验是老程序和小工具跑起来问题不大大型游戏和重度生产力软件就比较吃力了。5.2 实操心得与避坑指南折腾这套东西一年多踩过的坑比跑通的程序还多。分享几个最有价值的经验。第一版本匹配比什么都重要。Wine、FEX-Emu、DXMT 这三个组件的版本要匹配不能随便混用。FEX-Emu 的某个版本可能只兼容特定版本的 WineDXMT 又可能只支持特定版本的 FEX-Emu。我建议用项目官方推荐的版本组合不要自己乱升级。有一次我手贱把 FEX-Emu 升到最新版结果 Wine 直接启动不了回退版本才恢复。第二日志是你的好朋友。Wine 的WINEDEBUG环境变量、FEX-Emu 的FEX_LOG_LEVEL、DXMT 的日志输出这三个日志源结合起来看基本能定位 95% 的问题。我习惯在启动脚本里把日志重定向到文件出问题的时候直接翻日志比瞎猜快多了。export WINEDEBUGall export FEX_LOG_LEVELinfo FEXInterpreter wine program.exe wine.log 21第三不要追求一次跑通所有程序。每个 Windows 程序的依赖和运行环境都不一样有的需要 .NET有的需要 Visual C 运行库有的需要特定的 DirectX 版本。我的做法是给每个程序单独建一个 Wine 前缀用winetricks安装对应的依赖互不干扰。虽然占硬盘空间但稳定性好很多。第四性能调优要循序渐进。先把程序跑起来再考虑优化。一开始就折腾各种性能参数很容易把环境搞乱最后连程序都启动不了。我的顺序是先保证能跑再保证稳定最后才调性能。第五备份你的 Wine 前缀。配置好的 Wine 前缀是心血结晶一旦损坏重新配置很痛苦。我习惯用tar打包备份改配置之前先备份一份出问题直接恢复。tar -czf wine-prefix-backup.tar.gz ~/madeira-wine这套方案后续还可以往几个方向扩展。一是自动化配置脚本把常用的配置步骤写成脚本一键部署。二是容器化用 Docker 或者 Podman 把整个环境打包成镜像方便迁移和分发。三是图形化配置工具给不熟悉命令行的用户提供一个界面来管理 Wine 前缀和 FEX-Emu 参数。这些方向我都在慢慢尝试有进展再分享。
返回列表