尧图精选

Madeira 跨架构运行方案:FEX-Emu 与 Wine 让 x86-64 程序跑在 ARM 上

🕒 发布时间:2026/10/1 9:17:09 📁 来源:尧图网络
1. 从“Madeira”这个名字说起一个被低估的跨架构运行方案第一次看到“Madeira”这个词大多数人会想到那个盛产葡萄酒的葡萄牙海岛。但在系统兼容与跨平台运行这个圈子里它指向的是另一件事一套围绕FEX-Emu构建的、让 x86-64 程序在 ARM 设备上跑起来的运行环境方案。我最初接触它是因为手头有一台 ARM 架构的开发板想跑一些只有 x86-64 版本的老工具又不想为此专门再买一台机器。翻了不少资料之后发现 Madeira 这条路线在社区里讨论得不算多但实际用下来它的思路和落地效果都值得单独拿出来讲一讲。先把定位说清楚。Madeira 不是一个独立的软件产品更像是一套“组合拳”式的方案底层用FEX-Emu做指令集翻译把 x86-64 的机器指令实时翻译成 ARM64 能执行的指令中间层借助Wine提供 Windows 程序的运行环境再往上通过DXMT之类的组件处理图形接口的转换。这三者叠在一起目标就是让原本为 Windows x86-64 编译的程序在一台 ARM 设备上尽可能顺畅地跑起来。关键词里出现的 FEX-Emu、Wine、DXMT、x86-64基本就是这套方案的技术骨架。那它到底解决了什么问题简单说就是架构鸿沟。现在 ARM 设备越来越多从轻薄本到开发板再到各种嵌入式盒子性能不差、功耗还低但软件生态长期被 x86-64 占据。很多专业工具、老游戏、行业软件只有 x86-64 版本厂商也没有动力重新编译。Madeira 这类方案的价值就是不去要求软件适配硬件而是让硬件去“理解”软件。这跟当年 Wine 让 Linux 跑 Windows 程序是同一个逻辑只不过这次多了一层指令集翻译。适合谁来参考三类人最相关。第一类是手里有 ARM 设备、想扩展可用软件范围的技术爱好者第二类是做国产化适配、需要在非 x86 平台上跑存量 Windows 应用的工程师第三类是对指令集翻译、二进制兼容层感兴趣、想动手研究底层机制的人。如果你只是想在普通 x86 电脑上跑 Windows 软件那这套方案对你意义不大直接用 Wine 或者虚拟机就行。Madeira 的战场是 ARM 这一侧。需要提前说明的是下面涉及的具体配置和参数一部分来自我自己的实测记录一部分是基于 FEX-Emu 和 Wine 社区常见实践做的合理补充。不同发行版、不同内核版本、不同 ARM 芯片之间差异不小照搬之前建议先确认自己的环境。2. FEX-Emu 到底在做什么指令集翻译的底层逻辑2.1 为什么不能直接运行 x86-64 程序要理解 Madeira 为什么需要 FEX-Emu得先明白一个基本事实CPU 只认自己那套指令集。ARM64 的芯片看不懂 x86-64 的机器码就像只会中文的人看不懂俄语原文一样。你双击一个 x86-64 的 exe 或 ELF 文件ARM 芯片拿到那一串字节完全不知道该怎么执行。这不是操作系统的问题是硬件层面的语言不通。传统的解决办法有两种。一种是模拟比如 QEMU 的全系统模拟它把整个 CPU 的行为都软件化一条一条解释执行兼容性好但慢得离谱跑个桌面都卡。另一种是翻译把 x86-64 指令动态转换成 ARM64 指令转换一次之后可以缓存复用速度比纯模拟快一个数量级。FEX-Emu 走的就是第二条路它属于动态二进制翻译Dynamic Binary Translation这一类。这里有个容易混淆的点FEX-Emu 翻译的是用户态程序不是整个操作系统。它不需要模拟一颗完整的 x86 CPU也不需要模拟主板、显卡这些硬件。程序发起系统调用的时候FEX-Emu 会把这些调用转发给宿主 Linux 内核去处理。所以它比全系统模拟轻量得多启动快、开销小但前提是宿主系统本身能提供程序需要的系统调用接口。2.2 FEX-Emu 的翻译流程拆解FEX-Emu 的工作流程我把它拆成几个阶段来看会更清楚。第一步是加载与识别。当你通过 FEX 启动一个 x86-64 程序时FEX 先接管这个进程读取程序的 ELF 头识别出它是 x86-64 架构然后开始逐段加载代码。这一步跟普通加载器做的事差不多只是加载完之后不直接跳过去执行而是进入翻译环节。第二步是基本块翻译。FEX 不会一条指令一条指令地翻译那样效率太低。它把代码切成一个个“基本块”basic block也就是一段没有分支跳转的连续指令序列然后整块翻译成 ARM64 指令。翻译结果会存进一个翻译缓存translation cache下次再执行到同一块代码直接取缓存就行不用重新翻译。这个缓存机制是性能的关键也是为什么第一次运行某个程序会感觉稍慢、后面就顺畅很多。第三步是寄存器映射。x86-64 有自己的一套寄存器ARM64 也有自己的一套数量、位宽、用途都不一样。FEX 需要在两套寄存器之间做映射把 x86-64 的寄存器状态映射到 ARM64 的寄存器上。这个映射策略直接影响性能做得好的话大部分操作都能在寄存器里完成做不好就得频繁读写内存速度立刻掉下来。第四步是系统调用转发。程序运行中难免要读文件、开网络、申请内存这些都要通过系统调用。FEX 拦截这些调用转换成宿主 Linux 能理解的形式再发出去。这里有个细节x86-64 和 ARM64 的系统调用号、参数传递方式都不一样FEX 要做一层适配。如果某个系统调用宿主不支持程序就会报错这也是兼容性问题的主要来源之一。2.3 翻译缓存的调优经验翻译缓存的大小和策略是实际使用中最值得调的几个参数之一。缓存太小程序跑着跑着就把旧翻译结果挤掉了后面又要重新翻译表现为运行一段时间后突然卡顿缓存太大又占内存。我一般会先看程序的实际内存占用再决定给缓存留多少空间。还有一个经验首次运行的“预热”很重要。刚启动一个复杂程序时FEX 要翻译大量代码CPU 占用会飙高这是正常的。等主要代码路径都翻译完、进了缓存负载就会降下来。所以评估性能的时候不要拿刚启动那几秒的数据下结论至少让它跑完一轮完整操作再看。注意FEX-Emu 的版本更新比较频繁不同版本对指令集的支持程度、缓存策略、性能表现都有差异。遇到奇怪的问题时先确认自己用的是不是较新的稳定版很多坑在新版里已经修掉了。3. Wine 层让 Windows 程序以为自己在 Windows 上3.1 Wine 不是模拟器是兼容层很多人第一次听到 Wine会以为它是个 Windows 模拟器。其实 Wine 的全称是“Wine Is Not an Emulator”它明确否认自己是模拟器。Wine 做的事情是重新实现 Windows 的 API。Windows 程序运行时会调用一大堆系统提供的函数比如创建窗口、读写注册表、加载 DLL。Wine 把这些函数用 Linux 的方式重新写了一遍程序调用的时候Wine 接住请求用 Linux 的能力去完成然后把结果按 Windows 期望的格式返回。这个机制的好处是不需要 Windows 系统本身也不需要虚拟化开销小。坏处是 Windows API 浩如烟海Wine 不可能 100% 实现总有一些冷门函数或者新版本才引入的接口没覆盖到程序跑到那里就崩了。所以 Wine 的兼容性是一个持续补全的过程社区一直在往里填坑。在 Madeira 这套方案里Wine 的角色是承上启下。往上看它给 Windows 程序提供运行环境往下看它自己也是跑在 FEX-Emu 翻译出来的 x86-64 环境里的。也就是说Wine 本身可能也是 x86-64 版本由 FEX 翻译执行。这就形成了一个两层结构FEX 翻译 x86-64 指令Wine 在这层之上提供 Windows API。两层叠加开销自然比单层大但对那些只有 Windows 版本的程序来说这是目前比较现实的路径。3.2 Wine 前缀与依赖管理Wine 有个核心概念叫prefix前缀你可以理解成“一个独立的 Windows 环境”。每个 prefix 里有自己的注册表、自己的 C 盘目录、自己安装的 DLL 和程序。为什么要分 prefix因为不同程序对环境的依赖经常冲突A 程序需要某个 DLL 的 1.0 版B 程序需要 2.0 版放一起就打架。给它们各自一个 prefix互不干扰。创建 prefix 的时候有个选择会影响后续兼容性用 32 位还是 64 位。现在主流是 64 位 prefix但很多老程序是 32 位的需要 32 位支持。Wine 提供了 WoW64 机制来在 64 位环境里跑 32 位程序但配置起来有讲究。我的建议是如果目标程序明确是 32 位的直接建 32 位 prefix 省事如果不确定或者要跑多个程序建 64 位 prefix 并确保 WoW64 支持装好。依赖管理是另一个大头。Windows 程序经常依赖Visual C 运行库、.NET Framework、DirectX这些东西。Wine 自带了一部分实现但往往不够需要额外装。社区常用的工具是 winetricks它能帮你自动下载安装这些依赖。但要注意winetricks 装的东西来源五花八门有些是微软官方 redistributable有些是社区打包的装之前最好心里有数。3.3 中文乱码问题的根因与处理关键词里出现了“wine 乱码”“wine 栏是乱码”这是 Wine 用户最常遇到的问题之一值得单独说。乱码的本质是字符编码和字体缺失。Windows 程序显示中文时通常依赖系统里的中文字体比如宋体、微软雅黑。Wine 环境里如果没装这些字体程序找不到字形就会显示成方块或者乱码。解决办法分两步。第一步是装字体。把 Windows 系统里的中文字体文件simsun.ttc、msyh.ttc 之类复制到 Wine prefix 的字体目录或者用 winetricks 装核心字体包。第二步是配编码。有些程序依赖特定的区域设置需要在 Wine 配置里把 locale 设成中文环境让程序以为自己在中文 Windows 上跑。我踩过的一个坑是字体装了但程序还是乱码。后来发现是字体注册没做。光把字体文件放进去不够还得让 Wine 的字体系统识别到它这通常要改注册表或者用工具注册。另外不同程序对字体的调用方式不一样有的直接按名字找“SimSun”有的走字体链接机制所以有时候要装好几套字体才能覆盖全。提示如果只是菜单栏乱码优先检查是不是缺了 UI 字体如果是文档内容乱码可能是编码问题跟字体关系不大。先定位清楚再动手别一股脑全装一遍。4. DXMT 与图形栈把 DirectX 调用翻译成 Vulkan4.1 图形接口是跨平台运行的老大难程序能跑起来是一回事画面能正常显示是另一回事。Windows 程序画图大多走DirectX尤其是 Direct3D。这套接口是微软自家的Linux 和 ARM 上根本没有原生支持。所以跨平台运行 Windows 程序图形接口的转换是绕不过去的坎。历史上这块有好几条路线。早期 Wine 用的是WineD3D把 Direct3D 调用翻译成 OpenGL。后来 Vulkan 兴起出现了DXVK把 D3D9/10/11 翻译成 Vulkan效率比 OpenGL 路线高不少成了现在的主流。再往后针对 D3D12 又有了VKD3D。而DXMT是这条线上比较新的一个成员它的定位和 DXVK 类似但在某些场景下有自己的一套实现思路。在 Madeira 这种 ARM FEX 的环境里图形栈会更复杂一层。因为 DXVK 或 DXMT 本身也是 x86-64 的库要经过 FEX 翻译才能跑。翻译过程中图形调用频繁、数据量大对性能影响明显。所以图形这块的配置往往是决定“能不能用”和“好不好用”的分水岭。4.2 DXMT 的接入方式与选择理由为什么在 Madeira 里会提到 DXMT 而不是只用 DXVK这跟具体场景有关。DXMT 在某些 Direct3D 版本的支持上、在某些驱动的配合上可能有更合适的表现。选择哪个取决于你要跑的程序用的是哪个版本的 DirectX以及你的 ARM 设备用的是什么 GPU 驱动。接入方式上通常是把 DXMT 的 DLL 文件放到 Wine prefix 的对应目录覆盖掉 Wine 自带的实现然后在 Wine 配置里指定用这个 DLL。具体来说D3D 相关的 DLL 有 d3d11.dll、dxgi.dll 这些替换的时候要对应好版本。替换完还要设置环境变量告诉 Wine 走哪条翻译路径。这里有个实操细节DLL 覆盖的顺序和优先级。Wine 加载 DLL 时会先看 prefix 里有没有再看系统目录。如果你放的位置不对或者名字不对Wine 还是用自带的你以为换了其实没换。验证方法是看日志Wine 启动时会打印加载了哪些 DLL从日志里能确认到底用的是哪个。4.3 图形性能调优的几个抓手图形性能调优我一般从几个地方入手。驱动层。ARM 设备的 GPU 驱动质量参差不齐有的开源驱动对 Vulkan 支持好有的闭源驱动性能强但兼容性差。先确认驱动本身没问题再谈上层优化。用 vulkaninfo 之类的工具看看 Vulkan 支持到什么程度心里有个底。翻译缓存与着色器缓存。图形程序会产生大量着色器每次翻译着色器都很耗时。DXVK 和 DXMT 都支持着色器缓存把翻译好的着色器存下来下次直接用。这个缓存一定要开而且要给它足够的磁盘空间。第一次跑某个程序会卡很大一部分时间花在编译着色器上缓存建好之后就顺了。分辨率和画质设置。ARM 设备的 GPU 性能普遍不如同价位的独显所以别指望全高画质流畅跑。适当降分辨率、关掉一些特效帧数能上来不少。这不是妥协是认清硬件定位。FEX 与图形的配合。图形调用经过 FEX 翻译会有额外开销所以 FEX 的配置也会影响图形性能。比如翻译缓存的策略、线程模型的选择都可能间接影响帧数。这块需要结合具体程序调没有万能参数。5. 从零搭一套 Madeira 环境的实操路径5.1 环境确认与前置准备动手之前先把环境摸清楚。需要确认的几件事CPU 架构是不是 ARM64用 uname -m 看显示 aarch64 就对了内核版本够不够新FEX 对内核有些要求太老的版本可能缺特性发行版是什么不同发行版的包管理和依赖处理方式不同GPU 和驱动情况决定图形栈怎么配。前置依赖主要是编译工具链和运行库。FEX-Emu 如果从源码编译需要 CMake、编译器、各种开发库。如果发行版有现成包优先用包管理装省事。Wine 同理很多发行版仓库里就有但版本可能偏旧追求新特性的话得自己编或者用第三方源。我建议先在一个干净的环境里试别直接在生产系统上折腾。用容器或者虚拟机起一个 ARM64 的 Linux出问题了推倒重来成本低。等整套流程跑通了再考虑往正式环境迁移。5.2 FEX-Emu 的安装与验证安装 FEX-Emu如果发行版有包直接装。没有的话从源码来大致流程是拉代码、配 CMake、编译、安装。编译参数里有个关键项是目标架构要确保是给 ARM64 编的。编完之后FEX 会提供一个启动器通常叫 FEXLoader 或者 fex 之类的名字。验证是否装好最简单的办法是拿一个 x86-64 的小程序试跑。比如找个静态编译的 x86-64 hello world用 FEX 启动看能不能正常输出。能跑通说明翻译层基本工作正常。如果报错看错误信息是缺库还是翻译失败对症处理。这一步有个常见问题动态链接库找不到。x86-64 程序依赖的库也是 x86-64 的ARM 系统里没有需要额外准备。FEX 社区通常提供一套 rootfs里面包含了常见的 x86-64 库把它挂载或者配置到库搜索路径里程序才能找到依赖。5.3 Wine 的部署与 prefix 初始化FEX 跑通之后装 Wine。注意要装x86-64 版本的 Wine因为它是被 FEX 翻译执行的对象。装好之后用 winecfg 初始化一个 prefix。第一次运行 winecfg 会创建默认 prefix这个过程会花点时间耐心等。prefix 建好后先做几件基础配置设 Windows 版本有些程序对版本敏感设成它期望的版本、配字体把中文字体装上避免乱码、装运行库根据目标程序的需要用 winetricks 装 VC 运行库等。这些做完再装目标程序。装程序的时候用 wine 命令加安装包路径就行。安装过程中如果弹窗报错记下错误信息多半是缺依赖或者某个 API 没实现。缺依赖就补依赖API 没实现就比较麻烦可能要等 Wine 更新或者找替代方案。5.4 图形组件的替换与联调图形这块先把 DXMT 或 DXVK 的库准备好。从项目发布页下载对应版本的压缩包解压后把 DLL 文件复制到 prefix 的对应目录。复制之前建议备份原来的 DLL万一新组件有问题能快速回退。复制完设置环境变量。DXVK 和 DXMT 通常通过环境变量来控制行为比如指定用哪个 GPU、开不开缓存、日志级别等。这些变量可以在启动脚本里设也可以写进 Wine 的配置。设好之后启动程序观察画面是否正常、帧数如何。联调阶段最容易出问题的是版本匹配。DXMT 的某个版本可能只适配特定版本的 Wine 或特定版本的驱动版本对不上就各种异常。遇到问题先查项目文档的兼容性说明别盲目试。6. 实测中绕不开的坑与应对思路6.1 程序启动即崩从日志里找线索程序双击没反应或者一闪就退这是最常见的现象。这时候别急着重装先看日志。Wine 可以通过设置环境变量输出详细日志日志里会记录它加载了哪些 DLL、调用了哪些 API、在哪里出错。从最后几行往前看通常能定位到问题点。如果日志里出现“unimplemented function”之类的字样说明某个 Windows API 没实现这是 Wine 的兼容性边界只能等或者找 workaround。如果是“module not found”那是缺 DLL补上就行。如果是段错误可能是 FEX 翻译出了问题或者程序用了 FEX 还不支持的指令。我遇到过一次程序启动就崩日志显示是某个图形 DLL 加载失败。查下来是 DXMT 的版本和 Wine 版本不匹配换了个匹配的版本就好了。所以版本匹配这件事怎么强调都不过分。6.2 性能不达预期分层排查性能问题比崩溃更难查因为涉及 FEX、Wine、图形栈好几层。我的排查思路是分层定位。先看CPU 占用。如果 FEX 进程 CPU 跑满说明瓶颈在指令翻译可能是翻译缓存不够或者程序用了大量 FEX 不擅长的指令。这时候调 FEX 的缓存参数或者看看有没有更新的 FEX 版本优化了这块。再看GPU 占用。如果 GPU 跑满而 CPU 没满瓶颈在图形。降画质、降分辨率、检查驱动都是方向。如果 GPU 没跑满但帧数上不去可能是图形调用经过 FEX 翻译的开销太大或者着色器编译卡住了。还有一种情况是内存瓶颈。ARM 设备内存通常不大Wine FEX 程序本身加起来占用不小内存不够就会频繁换页表现也是卡。看看内存占用必要时加内存或者减少同时运行的程序。6.3 中文显示与输入法问题中文乱码前面说过字体层面的处理这里补充输入法的问题。Wine 环境里的输入法支持一直是个弱项中文输入经常出问题。基本的思路是让 Wine 走宿主系统的输入法框架但这需要配置而且不是所有程序都配合。如果目标程序对中文输入要求高建议先确认 Wine 版本对输入法的支持情况必要时用特定的输入法桥接方案。这块我没有特别通用的解法更多是具体问题具体调。一个经验是先保证显示正常再解决输入因为显示问题相对好定位输入问题牵扯的环节更多。6.4 系统调用不兼容导致的隐性故障有些程序不崩、不卡但功能不正常比如保存文件失败、网络连不上。这类问题往往是系统调用不兼容导致的。FEX 转发系统调用时如果宿主内核不支持某个调用或者语义有差异程序就会拿到意外的结果。排查这类问题可以 strace 跟踪系统调用看哪个调用返回了错误。找到之后查这个调用在 ARM64 上是什么情况是不是需要额外的兼容处理。有些问题可以通过配置内核参数或者装额外的兼容库解决有些则确实无解只能换方案。7. 这套方案适合谁以及后续可以怎么走把 Madeira 这套东西跑通之后我对它的定位有了更清晰的认识。它不是给普通用户准备的日常方案配置成本高、维护麻烦、兼容性看运气。但它对特定人群有实实在在的价值手里有 ARM 设备、又必须跑某些 x86-64 Windows 程序的人它提供了一条不换硬件的路。从技术演进的角度看FEX-Emu 这类翻译层的成熟度在提升Wine 的兼容性也在持续改善图形栈的翻译效率一年比一年好。这套组合的上限取决于这几个组件各自的发展速度以及它们之间的配合程度。我个人的体会是别指望它完美但可以期待它够用。很多场景下能跑起来、能完成基本任务就已经解决了大问题。后续如果继续深入有几个方向值得投入。一是针对具体程序做调优把常用程序的配置固化下来形成可复用的方案二是研究 FEX 的翻译策略看能不能针对特定负载做优化三是关注图形栈的新进展DXMT、DXVK 这些项目更新很快新版本往往带来明显的性能提升。这套东西不是搭完就一劳永逸的得跟着社区一起往前走。最后分享一个我自己的习惯每配好一个能正常跑的程序就把它的 prefix、配置、用到的组件版本打包备份。下次遇到类似程序直接拿这个备份改比从头配快得多。跨平台运行这事经验积累很重要踩过的坑记下来下次就能绕过去。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →