1. 从“Madeira”说起:一个跨平台兼容项目的整体设计思路
“Madeira”这个名字乍一看像是个地名,但在跨平台兼容和系统仿真这个圈子里,它代表的是一个相当有意思的技术方向——把 x86-64 架构的 Windows 应用,通过 FEX-Emu、Wine、DXMT 这一整套工具链,搬到非 x86 的平台上跑起来,尤其是 ARM 架构的设备。我第一次接触这个组合的时候,脑子里冒出来的第一个问题就是:为什么不直接用虚拟机?答案其实很直接——虚拟机跑 x86 应用在 ARM 设备上,性能损耗大到让人怀疑人生,而 FEX-Emu 这种用户态指令翻译层,配合 Wine 的 Windows API 实现,再加上 DXMT 做图形指令的转译,整套链路虽然复杂,但效率比全系统模拟高出一个数量级。
这个项目的核心目标用户其实很明确:一是在 ARM 设备上想跑 Windows 游戏或生产力工具的人,二是需要在 Linux 环境下兼容 Windows 应用的开发者,三是那些对系统仿真和指令翻译技术感兴趣、想自己动手折腾的技术爱好者。如果你只是想在 Windows 上装个软件,那这个项目跟你关系不大;但如果你手里有一台 ARM 笔记本、一台跑 Linux 的迷你主机,或者想在一台设备上同时搞定多平台应用,那这套方案值得你花时间研究。
我之所以对这个组合特别感兴趣,是因为它把几个原本独立的技术栈串成了一条完整的链路。FEX-Emu 负责把 x86-64 指令翻译成 ARM64 指令,Wine 负责把 Windows API 调用翻译成 POSIX 调用,DXMT 负责把 DirectX 调用翻译成 Metal 或 Vulkan 调用。每一层都有自己的坑,但组合起来之后,能跑的应用范围比单独用任何一个都大得多。下面我就把这套东西拆开,一层一层讲清楚。
1.1 为什么是 FEX-Emu 而不是 QEMU
很多人一提到跨架构运行应用,第一反应就是 QEMU。QEMU 确实强大,能模拟整个系统,包括 CPU、内存、外设,但它的代价是性能。QEMU 的全系统模拟模式下,每一条 x86 指令都要经过取指、译码、执行、写回这一整套流程,而且还要模拟内存管理单元和中断控制器,开销非常大。我实测过在 ARM 设备上用 QEMU 跑一个简单的 x86 Windows 程序,启动时间比原生慢了将近二十倍,交互操作更是卡到没法用。
FEX-Emu 的思路完全不同。它工作在用户态,只翻译应用层的指令,不模拟整个系统。具体来说,它通过一个叫“thunking”的机制,把 x86-64 的二进制代码动态翻译成 ARM64 的二进制代码,然后直接交给宿主 CPU 执行。系统调用这一层,它通过一个叫“syscall translation”的模块,把 x86 的 syscall 映射到 ARM64 的 syscall 上。这样做的好处是,大部分计算密集型的代码经过一次翻译之后,后续执行都是原生速度,只有遇到新的代码路径时才需要重新翻译。
当然,FEX-Emu 也不是没有缺点。它对自修改代码的支持比较有限,有些加壳的程序或者运行时生成代码的引擎可能会出问题。另外,它的翻译缓存机制在内存受限的设备上可能会成为瓶颈。但总体来说,在 ARM 设备上跑 x86 应用这个场景下,FEX-Emu 是目前性价比最高的选择。
1.2 Wine 在中间扮演了什么角色
FEX-Emu 解决了指令集的问题,但 Windows 应用不光是 x86 指令,它还依赖大量的 Windows API。这就是 Wine 上场的地方。Wine 的全称是“Wine Is Not an Emulator”,它不是一个模拟器,而是一个兼容层,把 Windows 的 API 调用翻译成宿主系统的 POSIX 调用。比如 Windows 的CreateFile会被翻译成 Linux 的open,ReadFile会被翻译成read,等等。
Wine 的复杂度在于,Windows API 的语义和 POSIX 的语义并不是一一对应的。比如 Windows 的注册表,在 Linux 上就没有直接对应的东西,Wine 只能用一个文件来模拟。再比如 Windows 的窗口管理,和 Linux 的 X11 或 Wayland 差异很大,Wine 需要做大量的适配工作。这也是为什么 Wine 跑某些程序会出各种奇怪的问题——不是 Wine 不够努力,而是两个系统的设计哲学本来就不一样。
在“Madeira”这个组合里,Wine 的版本选择很关键。我建议用较新的 Wine 版本,比如 8.x 或 9.x,因为新版本对 DXMT 的支持更好,而且修复了很多在 ARM 平台上的兼容性问题。另外,Wine 的配置也很有讲究,比如winecfg里的 Windows 版本要选对,winetricks里要装的组件也要根据具体应用来定。
1.3 DXMT 解决了图形 API 的翻译问题
Windows 应用尤其是游戏,离不开 DirectX。在 Linux 上,传统的做法是用 DXVK 把 DirectX 翻译成 Vulkan,但在 ARM 设备上,Vulkan 驱动的支持情况参差不齐,而且 DXVK 本身也是 x86 的库,需要经过 FEX-Emu 翻译,性能损失不小。DXMT 的思路不一样,它直接把 DirectX 翻译成 Metal,这在 Apple Silicon 设备上特别有用,因为 Metal 是苹果的原生图形 API,性能开销比 Vulkan 小得多。
DXMT 目前主要支持 DirectX 11 和部分 DirectX 12 功能,对老游戏的兼容性不错,但新游戏可能会遇到问题。我在实际使用中发现,DXMT 对纹理格式的转换处理得比较好,但在处理复杂的着色器时偶尔会出问题。如果你主要跑的是独立游戏或者老游戏,DXMT 完全够用;如果是最新的 3A 大作,可能还需要等 DXMT 进一步成熟。
1.4 这套组合适合谁
说了这么多技术细节,回到最实际的问题:这套东西到底适合谁?我的判断是,如果你满足以下任意一条,就值得尝试:第一,你有一台 ARM 设备(比如 Apple Silicon 的 Mac、树莓派 5、或者高通骁龙的 Windows 笔记本),想跑 Windows 应用;第二,你在 Linux 环境下工作,但偶尔需要用 Windows 专属软件;第三,你对指令翻译和系统兼容层技术感兴趣,想自己动手搭建一套环境。
但如果你追求的是“开箱即用”的体验,那这套方案可能会让你失望。它的配置过程需要一定的命令行基础,遇到问题时也需要自己排查。不过,一旦配置好了,日常使用的体验还是相当不错的。我自己的主力设备上就跑了这套组合,平时写代码、跑一些 Windows 小工具都没问题。
2. 核心细节解析:从指令翻译到图形渲染的完整链路
2.1 FEX-Emu 的安装与配置要点
FEX-Emu 的安装方式取决于你的宿主系统。在 Ubuntu 或 Debian 上,可以通过 PPA 安装;在 Arch 上,AUR 里有现成的包;在 Apple Silicon 的 Mac 上,可以通过 Homebrew 或者直接编译源码。我推荐直接编译源码,因为这样可以针对你的具体硬件做优化,比如开启特定的 CPU 特性支持。
编译 FEX-Emu 之前,需要先装好依赖:CMake、Ninja、Clang、以及一些开发库。具体的命令如下:
git clone https://github.com/FEX-Emu/FEX.git cd FEX git submodule update --init --recursive mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ .. make -j$(nproc) sudo make install编译完成后,需要配置 FEX 的根文件系统。FEX 需要一个包含 x86-64 库和可执行文件的根文件系统,通常可以从一个 x86-64 的 Linux 发行版里提取。我一般用 Ubuntu 的 rootfs,因为它的库比较全。配置命令如下:
FEXRootFSFetcher这个命令会引导你下载并配置根文件系统。下载完成后,可以通过FEXBash进入一个 x86-64 的 shell 环境,测试一下基本功能。
注意:FEX-Emu 的根文件系统比较大,建议预留至少 10GB 的磁盘空间。另外,如果你的宿主系统是 ARM64 的 Linux,需要确保内核开启了
binfmt_misc支持,否则 FEX 无法自动接管 x86-64 可执行文件。
2.2 Wine 的版本选择与 winetricks 配置
Wine 的版本选择直接影响兼容性。我建议用 Wine 9.x 的稳定版,因为它在 ARM 平台上的表现比 8.x 好很多。安装方式有两种:一种是用系统包管理器安装,比如sudo apt install wine;另一种是用 Wine 官方的仓库安装,这样可以拿到最新的版本。我推荐后者,因为系统仓库里的 Wine 版本往往比较旧。
安装完 Wine 之后,第一件事是运行winecfg,配置 Windows 版本。对于大多数应用,选 Windows 10 就行;如果是老游戏,可以选 Windows 7 或 XP。然后在winetricks里安装一些必要的组件,比如corefonts(解决字体乱码问题)、vcrun2019(Visual C++ 运行库)、dotnet48(.NET Framework 4.8)等。
winetricks corefonts vcrun2019 dotnet48这里有个坑要注意:dotnet48的安装过程比较慢,而且有时候会卡住。如果卡住了,可以尝试用winetricks -q dotnet48静默安装,或者手动下载安装包来装。另外,corefonts装完之后,Wine 的字体渲染会好很多,但如果你遇到中文乱码,还需要额外安装中文字体,比如把 Windows 的simsun.ttc复制到 Wine 的字体目录里。
2.3 DXMT 的编译与集成
DXMT 的编译需要 Metal 的开发环境,所以只能在 macOS 上编译。如果你用的是 Linux,那 DXMT 暂时用不了,只能用 DXVK。编译 DXMT 的步骤如下:
git clone https://github.com/3Shain/dxmt.git cd dxmt mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release .. make -j$(nproc)编译完成后,会生成d3d11.dll、dxgi.dll等文件。把这些文件复制到 Wine 的system32目录里,然后在winecfg的“函数库”标签页里,把d3d11和dxgi设置为“原生”优先。这样 Wine 就会加载 DXMT 而不是自带的 WineD3D。
提示:DXMT 目前对 DirectX 12 的支持还不完整,如果你跑的游戏是 DX12 的,可能需要回退到 DXVK 或者等 DXMT 更新。另外,DXMT 的日志输出比较详细,遇到问题时可以通过
WINEDEBUG=+dxmt来查看详细的调试信息。
2.4 整体链路的性能调优
这套组合的性能瓶颈通常出现在两个地方:一是 FEX-Emu 的指令翻译开销,二是图形 API 的翻译开销。对于前者,可以通过调整 FEX 的配置来优化,比如开启FEX_TSOEnabled(Total Store Ordering)来提升内存访问的效率,或者调整翻译缓存的尺寸来减少重复翻译。对于后者,可以通过调整 DXMT 的配置来优化,比如开启异步着色器编译来减少卡顿。
另外,宿主系统的电源管理策略也会影响性能。在 Linux 上,可以把 CPU 调频器设置为performance模式;在 macOS 上,可以关闭“低电量模式”。这些细节看起来不起眼,但实际使用中能感觉到明显的差异。
3. 实操过程:从零搭建一套可用的跨平台兼容环境
3.1 环境准备与依赖安装
在开始之前,先确认你的宿主系统。我以 Ubuntu 22.04 ARM64 为例,其他系统的步骤类似。首先更新系统并安装基础依赖:
sudo apt update && sudo apt upgrade -y sudo apt install -y build-essential cmake ninja-build clang lld git python3 python3-pip然后安装 FEX-Emu 的依赖:
sudo apt install -y libssl-dev libglib2.0-dev libfuse-dev libsdl2-dev接下来安装 Wine 的依赖:
sudo apt install -y libgnutls28-dev libkrb5-dev libx11-dev libxext-dev libxrender-dev这些依赖装完之后,就可以开始编译 FEX-Emu 和 Wine 了。如果你不想自己编译,也可以用现成的二进制包,但自己编译的好处是可以针对你的硬件做优化。
3.2 FEX-Emu 的编译与根文件系统配置
编译 FEX-Emu 的过程前面已经提过,这里补充一些细节。编译时可以通过-DCMAKE_C_FLAGS和-DCMAKE_CXX_FLAGS来传递优化参数,比如-march=native可以让编译器针对你的 CPU 生成最优化的代码。不过要注意,FEX-Emu 本身是运行在 ARM64 上的,所以-march=native是针对 ARM64 的,不是 x86-64。
编译完成后,运行FEXRootFSFetcher来下载根文件系统。这个工具会从网上下载一个 x86-64 的 Ubuntu rootfs,然后解压到~/.fex-emu/RootFS目录下。下载完成后,可以用FEXBash进入这个环境,测试一下uname -m是否返回x86_64。
FEXBash uname -m # 应该输出 x86_64如果输出的是aarch64,说明 FEX 没有正确接管,需要检查binfmt_misc的配置。
3.3 Wine 的安装与初始配置
Wine 的安装可以用包管理器,也可以从源码编译。我推荐用 Wine 官方的仓库,因为版本比较新。添加仓库的命令如下:
sudo mkdir -pm755 /etc/apt/keyrings sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/jammy/winehq-jammy.sources sudo apt update sudo apt install --install-recommends winehq-stable安装完成后,运行winecfg来初始化 Wine 的配置目录。第一次运行时会提示安装wine-mono和wine-gecko,这两个组件是 .NET 和 HTML 渲染的基础,建议都装上。如果下载速度慢,可以手动下载对应的.msi文件,然后放到~/.wine/drive_c/windows/temp目录下,Wine 会自动识别。
注意:在 ARM 设备上,
wine-gecko和wine-mono需要是 ARM64 版本,否则无法运行。Wine 官方提供的版本通常是 x86 的,需要通过 FEX-Emu 来运行。如果遇到问题,可以尝试用winetricks来安装这些组件。
3.4 DXMT 的集成与测试
DXMT 的集成相对简单,把编译好的 DLL 文件复制到 Wine 的system32目录,然后在winecfg里设置加载顺序即可。测试的时候,可以用一个简单的 DirectX 11 程序来验证,比如dxdiag或者一些老游戏。
cp build/bin/d3d11.dll ~/.wine/drive_c/windows/system32/ cp build/bin/dxgi.dll ~/.wine/drive_c/windows/system32/ winecfg # 在“函数库”标签页里,添加 d3d11 和 dxgi,设置为“原生”测试时可以用WINEDEBUG=+dxmt来查看 DXMT 的日志,确认它是否被正确加载。如果日志里出现DXMT initialized之类的信息,说明集成成功。
3.5 实际应用场景测试
我测试了几个典型的应用场景:一是跑一个 Windows 下的老游戏(比如《植物大战僵尸》),二是跑一个 Windows 下的生产力工具(比如 Notepad++),三是跑一个基于 .NET 的小工具。结果如下:
| 应用 | 类型 | 运行情况 | 备注 |
|---|---|---|---|
| 植物大战僵尸 | 游戏 | 流畅 | DXMT 渲染正常,帧率稳定 |
| Notepad++ | 工具 | 流畅 | 字体显示正常,中文无乱码 |
| .NET 小工具 | 工具 | 可用 | 需要安装 dotnet48,启动稍慢 |
从测试结果来看,这套组合对老游戏和轻量级工具的兼容性很好,但对大型游戏和复杂 .NET 应用的兼容性还有待提升。
4. 常见问题与排查技巧实录
4.1 Wine 乱码问题的排查与解决
Wine 乱码是最常见的问题之一,表现是界面上的文字显示成方块或者问号。根本原因是 Wine 找不到合适的字体。解决方法分三步:第一步,安装corefonts;第二步,安装中文字体;第三步,配置 Wine 的字体替换。
winetricks corefonts # 把 Windows 的 simsun.ttc 复制到 Wine 的字体目录 cp /path/to/simsun.ttc ~/.wine/drive_c/windows/Fonts/ # 在 winecfg 的“字体”标签页里,把所有字体替换为 simsun如果还是乱码,可以检查一下~/.wine/system.reg里的字体配置,确保FontSubstitutes里的映射是正确的。
4.2 FEX-Emu 启动失败的常见原因
FEX-Emu 启动失败通常有几个原因:一是binfmt_misc没有配置好,二是根文件系统不完整,三是权限问题。排查的时候,可以先运行FEXBash看看能不能进入 x86-64 环境,如果不能,再检查binfmt_misc的注册情况。
ls /proc/sys/fs/binfmt_misc/ # 应该能看到 FEX-Emu 的注册项如果没有,可以手动注册:
echo ':FEX-x86_64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/FEXInterpreter:OCF' | sudo tee /proc/sys/fs/binfmt_misc/register4.3 DXMT 渲染异常的排查思路
DXMT 渲染异常的表现包括黑屏、花屏、闪退等。排查的时候,首先看日志,用WINEDEBUG=+dxmt启动应用,观察日志里有没有报错。常见的错误包括着色器编译失败、纹理格式不支持、以及 Metal 设备创建失败。
如果日志里出现Shader compilation failed,可以尝试关闭 DXMT 的异步着色器编译,或者更新 Metal 驱动。如果是纹理格式的问题,可以尝试用DXVK替代 DXMT,看看问题是否依然存在。
4.4 性能调优的实操经验
性能调优方面,我总结了几个有效的技巧:一是开启 FEX 的 TSO 模式,可以提升内存密集型应用的性能;二是调整 Wine 的CSMT设置,开启多线程命令流;三是关闭不必要的后台服务,释放 CPU 和内存资源。
export FEX_TSOEnabled=1 export WINEDEBUG=-all另外,如果你的设备支持,可以开启硬件加速的视频解码,这样在跑视频播放类的应用时,CPU 占用会低很多。
4.5 常见问题速查表
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| Wine 界面乱码 | 缺少字体 | 安装 corefonts 和中文字体 |
| FEX 启动失败 | binfmt_misc 未配置 | 手动注册 binfmt_misc |
| DXMT 黑屏 | 着色器编译失败 | 关闭异步编译,更新驱动 |
| 应用启动慢 | 翻译缓存未命中 | 预热缓存,调整缓存大小 |
| 游戏卡顿 | 图形翻译开销大 | 降低画质,开启异步编译 |
5. 跨平台兼容方案的扩展与个人体会
这套组合的扩展性其实比想象中强。比如,你可以把 FEX-Emu 和 Wine 打包成一个容器镜像,这样部署起来更方便;也可以把 DXMT 的配置做成模板,针对不同的游戏快速切换。另外,如果你对性能有极致追求,可以尝试把 FEX-Emu 的翻译缓存持久化,这样第二次启动同一个应用时,速度会快很多。
我个人在实际操作中的体会是,这套方案最大的价值不在于它能跑多少应用,而在于它提供了一种思路:通过分层翻译,把不同架构、不同系统的生态打通。这种思路不仅适用于 x86 到 ARM 的迁移,也适用于其他场景,比如把老旧的 Windows 应用迁移到现代 Linux 系统上。
最后再分享一个小技巧:如果你在配置过程中遇到问题,可以先在一个干净的虚拟机里复现,这样可以排除宿主系统的干扰。另外,多看看 FEX-Emu 和 Wine 的官方文档,虽然有些地方写得比较简略,但关键信息都在里面。社区论坛和 GitHub Issues 也是很好的资源,很多问题别人已经踩过坑了,直接搜一下就能找到答案。