ARTICLE DETAIL

资讯详情

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

Madeira 实战:在 ARM Linux 上运行 Windows 应用

Madeira 实战:在 ARM Linux 上运行 Windows 应用 1. 项目缘起为什么要在 Linux 上折腾 Windows 应用兼容层第一次接触 Madeira 这个项目是在给一台老旧的 ThinkPad 装完某个国产 Linux 发行版之后。那台机器配置不高但日常办公、写代码、看文档都够用唯独有几个 Windows 下用惯了的工具找不到替代品。当时试过虚拟机方案资源占用太高风扇呼呼转也试过一些商业兼容软件授权费用不便宜而且对老硬件的支持并不理想。后来在社区里看到有人提到 Madeira说是把 Wine 和 FEX-Emu 这两套东西整合到了一起专门解决 x86-64 Windows 应用在 ARM 架构 Linux 上的运行问题这才算找到了方向。Madeira 这个名字本身挺有意思它不是一个从零开始造轮子的项目而是一个“胶水层”或者说“整合方案”。它的核心思路很直接底层用 FEX-Emu 做指令集翻译把 x86-64 的机器码转换成 ARM64 能执行的指令中间用 Wine 提供 Windows API 的兼容实现上层再通过 DXMT 把 Direct3D 调用转译成 Metal 或者 Vulkan让图形应用和游戏能跑起来。这三者单独拿出来都不算新东西但把它们串成一条可用的链路并且针对特定场景做调优这就是 Madeira 的价值所在。适合关注这个项目的人其实挺明确的。一类是使用 ARM 架构设备比如某些国产笔记本、开发板、甚至手机平板但又有 Windows 应用刚需的用户另一类是对系统兼容层技术感兴趣想研究指令翻译、API 转译、图形栈适配的开发者还有一类就是像我这样手头有老设备不想扔想榨干最后一点剩余价值的人。不管你属于哪一类理解 Madeira 的架构和实操细节都能帮你少走很多弯路。2. 核心架构拆解FEX-Emu、Wine、DXMT 各自扮演什么角色2.1 FEX-Emu把 x86-64 指令“翻译”成 ARM64 能听懂的话FEX-Emu 是整个链路里最底层的一环它解决的是 CPU 指令集不兼容的问题。ARM 设备跑的是 AArch64 指令而 Windows 应用编译出来的是 x86-64 指令两者根本不在一个频道上。FEX-Emu 的做法是动态二进制翻译也就是在程序运行时把 x86-64 指令块实时转换成 ARM64 指令块然后交给 CPU 执行。这个过程听起来开销很大但 FEX-Emu 做了大量优化比如指令块缓存、寄存器映射、条件码优化等等实际用下来对于办公类应用和轻度游戏性能损失在可接受范围内。这里有个关键点需要说清楚FEX-Emu 不是模拟器它不模拟硬件环境而是做指令翻译。模拟器比如 QEMU 那种会完整模拟一套 x86 硬件包括 CPU、内存控制器、外设等等开销巨大。FEX-Emu 只翻译指令系统调用和硬件访问还是走原生 Linux 内核所以效率高很多。你可以把它理解成一个“同声传译”而不是“重新演一遍”。在实际配置中FEX-Emu 有几个参数值得关注。FEX_APP_CONFIG环境变量可以指定配置文件路径里面能调整 JIT 缓存大小、线程数、日志级别等。对于内存较小的设备建议把 JIT 缓存调小一点比如 64MB 到 128MB避免占用过多内存导致系统卡顿。另外FEX_SILENTLOG可以关闭冗余日志输出提升一点性能。这些细节在官方文档里不一定写得很显眼但实测下来对体验影响不小。2.2 Wine提供 Windows API 的“翻译字典”Wine 大家应该都不陌生它的全称是“Wine Is Not an Emulator”虽然名字里有 Emulator但它确实不是模拟器。Wine 实现了一套 Windows API 的兼容层把 Windows 程序调用的CreateWindow、MessageBox、RegOpenKey这些函数映射到 Linux 对应的 X11、Wayland、文件系统、注册表模拟等机制上。程序以为自己是在跟 Windows 内核打交道实际上是在跟 Wine 的库函数打交道。在 Madeira 的架构里Wine 跑在 FEX-Emu 之上。也就是说Windows 应用的 x86-64 指令先被 FEX-Emu 翻译成 ARM64 指令然后这些指令里对 Windows API 的调用再被 Wine 拦截并转换成 Linux 系统调用。这个链路是Windows 应用 - FEX-Emu 指令翻译 - Wine API 转换 - Linux 内核。每一层都有开销但每一层也都有优化空间。Wine 的配置是个细活。WINEPREFIX环境变量决定了 Wine 的“虚拟 C 盘”放在哪里建议每个应用单独一个 prefix避免不同应用之间的注册表和 DLL 冲突。WINEARCH一般设成win64因为现在大多数 Windows 应用都是 64 位的。还有WINEDLLOVERRIDES可以用来禁用或替换某些 DLL比如mscoreed可以禁用 .NET 相关的 DLL避免一些安装程序卡死。这些参数在调试阶段非常有用。2.3 DXMT把 Direct3D 调用转译成 Metal 或 VulkanDXMT 是 Madeira 里负责图形的那一环。Windows 应用和游戏通常调用 Direct3D 来渲染画面而 Linux 上主流图形 API 是 Vulkan 和 OpenGL苹果生态里是 Metal。DXMT 的作用就是把 D3D 调用转换成这些原生 API 能理解的命令。它跟 DXVK 和 VKD3D 是同类东西但 DXMT 更侧重于 Metal 后端这在某些 ARM 设备上更有优势因为那些设备的图形驱动对 Metal 的支持往往比 Vulkan 更成熟。DXMT 的配置主要在 Wine 的注册表里。你可以通过wine regedit添加键值来调整 DXMT 的行为比如HKEY_CURRENT_USER\Software\Wine\DXMT下面可以设置d3d11的maxFeatureLevel强制指定 D3D 特性等级。有些老游戏只支持 D3D9那就需要确保 DXMT 的 D3D9 转译路径是启用的。另外DXMT_SHADER_CACHE环境变量可以指定着色器缓存目录第一次运行游戏时会编译着色器之后就能直接加载缓存启动速度会快很多。注意DXMT 的版本要和 Wine 版本匹配版本错配可能导致图形初始化失败表现为黑屏或者闪退。建议从 Madeira 的官方仓库统一安装不要自己混搭不同来源的包。3. 实操部署从零搭建 Madeira 运行环境3.1 系统准备与依赖安装在开始之前先确认你的系统架构是 ARM64。可以用uname -m查看输出aarch64就对了。如果是 x86-64 系统其实不需要 FEX-Emu直接装 Wine 就行Madeira 的意义就不大了。确认架构之后更新系统包管理器缓存然后安装基础依赖。以 Debian 系为例需要装build-essential、cmake、ninja-build、python3、pkg-config、libgl1-mesa-dev、libvulkan-dev、libsdl2-dev、libgnutls28-dev这些。不同发行版包名可能略有差异但大体差不多。接下来是获取 Madeira 的源码或预编译包。如果追求省事可以直接用社区维护的预编译包但要注意版本和系统匹配。如果追求可控性就从源码编译。源码编译的大致流程是先编译 FEX-Emu再编译 Wine需要打上 Madeira 的补丁最后编译 DXMT。每一步都要确保依赖完整否则会在链接阶段报一堆找不到符号的错误。编译 FEX-Emu 时cmake配置阶段可以加上-DENABLE_JITON和-DENABLE_CACHEON这两个选项对性能影响很大。提示编译过程比较吃内存建议至少 8GB 内存否则链接阶段容易 OOM。如果内存不够可以临时增加 swap 分区或者用-j2限制并行编译任务数。3.2 Wine prefix 的创建与调优装好之后第一件事是创建 Wine prefix。命令是WINEPREFIX~/.wine-madeira WINEARCHwin64 wineboot -u。这个命令会初始化一个 64 位的 Wine 环境并安装一些基础组件。初始化完成后可以用winecfg打开配置界面调整 Windows 版本、驱动器映射、库覆盖等。建议把 Windows 版本设成 Windows 10兼容性最好。prefix 创建好之后有几个调优项值得做。第一关闭不必要的 Wine 服务比如winebrowser、winefile这些在后台跑着占资源。可以在winecfg的“服务”标签页里禁用。第二调整注册表里的DirectInput和DirectSound设置对于游戏应用把DirectInput的emulate模式打开可以兼容一些老游戏的手柄输入。第三如果应用需要 .NET可以用winetricks安装dotnet48或dotnet6但要注意 .NET 在 FEX-Emu 下的性能损耗比较大能不用就不用。3.3 安装 Windows 应用与常见问题处理安装 Windows 应用一般用wine setup.exe或者wine msiexec /i package.msi。安装过程中如果遇到乱码通常是字体缺失或者编码设置不对。解决办法是安装winetricks corefonts和winetricks cjkfonts把常用中文字体装进去。如果安装程序界面显示不全可以试试winecfg里把屏幕分辨率调低一点或者用虚拟桌面模式运行。安装完成后运行应用时可能会遇到 DLL 缺失的提示。这时候可以用winetricks安装对应的运行库比如vcrun2019、dotnet48、xna40等。但要注意不是所有运行库都能在 FEX-Emu 下正常工作有些会直接崩溃。遇到这种情况可以试试用WINEDLLOVERRIDES禁用相关 DLL或者找绿色版应用避免安装过程。注意在 ARM 设备上跑 Windows 应用性能瓶颈往往不在 CPU 翻译而在图形驱动和内存带宽。如果应用界面卡顿先检查是不是 DXMT 没有正确启用或者着色器缓存没有生效。4. 性能调优与兼容性排查实战4.1 性能调优从 JIT 缓存到着色器编译性能调优是个系统工程不能指望改一个参数就起飞。先说 FEX-Emu 这边JIT 缓存大小对性能影响很明显。缓存太小指令块频繁被淘汰翻译开销就上去了缓存太大内存占用高可能触发系统 swap反而更慢。我的经验是4GB 内存的设备设 64MB8GB 设 128MB16GB 以上设 256MB这个范围比较稳妥。另外FEX_TSO_ENABLED这个环境变量控制是否启用 x86 的内存一致性模型模拟对于多线程应用开启它能避免一些诡异的同步问题但会带来性能损失。如果应用对内存一致性要求不高可以关掉试试。Wine 这边的调优主要是减少不必要的系统调用和 DLL 加载。可以用WINEDEBUG-all关闭所有调试输出减少日志开销。WINEDLLOVERRIDES里把不用的 DLL 设成ddisabled能加快启动速度。还有winecfg里的“图形”标签页把“允许窗口管理器装饰窗口”关掉能减少一点合成开销。DXMT 这边的调优重点是着色器缓存。第一次运行应用时DXMT 会编译着色器这个过程可能很慢但编译结果会缓存下来。确保DXMT_SHADER_CACHE指向一个可写目录并且不要频繁清理这个目录。如果应用更新了图形内容缓存可能会失效需要重新编译这是正常现象。4.2 兼容性排查常见错误与解决思路兼容性问题千奇百怪但有一些是高频出现的。下面这个表格整理了我遇到过的一些典型问题以及对应的排查思路和解决办法。问题现象可能原因排查方法解决办法应用启动即闪退FEX-Emu 翻译失败或 Wine DLL 缺失用WINEDEBUGloaddll查看加载了哪些 DLL安装缺失的运行库或禁用冲突 DLL界面乱码字体缺失或编码错误检查~/.wine/drive_c/windows/Fonts目录安装corefonts和cjkfonts图形黑屏DXMT 未启用或版本不匹配查看 Wine 日志里是否有 DXMT 初始化信息重新安装匹配版本的 DXMT音频无声Wine 音频驱动未配置运行winecfg检查音频标签页切换音频驱动为 PulseAudio 或 ALSA性能极差JIT 缓存过小或 TSO 开启监控 CPU 和内存占用调整 JIT 缓存大小关闭 TSO安装程序卡死.NET 或 VCRun 安装失败查看安装日志用winetricks单独安装运行库排查的时候日志是最好的朋友。WINEDEBUG可以组合多个通道比如WINEDEBUGloaddll,d3d,dxmt这样能同时看到 DLL 加载、D3D 调用和 DXMT 转译的详细信息。但日志量会很大建议重定向到文件再慢慢看。另外FEX_LOG_LEVEL可以控制 FEX-Emu 的日志级别调试阶段设成info或debug生产环境设成error或silent。4.3 实操心得那些文档里不会写的坑第一个坑是文件系统大小写敏感。Linux 文件系统默认区分大小写而 Windows 应用经常不区分。Wine 默认会做大小写不敏感映射但有些应用会绕过 Wine 的映射直接访问文件导致找不到文件。解决办法是在winecfg的“驱动器”标签页里把驱动器类型设成“自动检测”或者手动指定为“CD-ROM”来强制大小写不敏感。第二个坑是线程调度。FEX-Emu 翻译后的代码在 ARM 上跑线程调度策略跟原生 x86 不一样。有些应用对线程优先级很敏感在 ARM 上可能表现异常。可以试试用taskset把应用绑定到特定核心或者用nice调整优先级。我遇到过某个应用在默认调度下卡顿绑定到大核之后流畅很多。第三个坑是内存对齐。x86 和 ARM 对内存对齐的要求不同有些应用在 x86 上能跑在 ARM 上就段错误。这种问题很难排查通常需要看 FEX-Emu 的日志里有没有对齐相关的警告。如果确认是对齐问题可以试试在 FEX-Emu 配置里开启对齐检查模拟但性能会下降。提示遇到诡异问题时先试试更新 FEX-Emu 和 Wine 到最新版本。很多兼容性问题在新版本里已经修复了没必要自己硬啃。5. 应用场景与扩展玩法5.1 办公与生产力工具的运行体验办公类应用是 Madeira 最典型的应用场景之一。我实测过几个常用的办公软件整体体验可以用“能用”来形容但离“好用”还有距离。文字处理类应用启动速度尚可打字延迟不明显但复杂排版和大型文档滚动时会有轻微卡顿。表格类应用在公式计算密集的场景下CPU 占用会飙升因为 FEX-Emu 翻译浮点运算指令的开销比较大。演示文稿类应用在播放动画时如果 DXMT 没有正确启用会掉帧严重。对于生产力工具我的建议是尽量找 Linux 原生替代品。如果实在找不到再用 Madeira 跑 Windows 版。跑的时候把不用的功能关掉比如自动更新、云同步、插件市场这些都会在后台消耗资源。另外把应用的缓存目录映射到 tmpfs 上能减少磁盘 I/O提升响应速度。5.2 游戏与图形应用的兼容性现状游戏是另一个热门场景但也是挑战最大的场景。轻量级 2D 游戏和老款 3D 游戏在 Madeira 下通常能跑帧率取决于设备性能。我在一台 ARM 开发板上试过几个经典游戏DXMT 转译 D3D9 的效果不错基本能稳定 30 帧。但 D3D11 和 D3D12 的游戏就吃力很多着色器编译慢复杂场景掉帧明显。图形应用方面一些基于 OpenGL 的老版本软件能跑但基于 Vulkan 的新版本往往有问题因为 DXMT 对 Vulkan 后端的支持还在完善中。如果你主要用图形应用建议优先考虑 Linux 原生版本或者用 Web 版替代。Madeira 跑图形应用更适合作为临时方案而不是长期主力。5.3 在 ARM 设备上的部署注意事项ARM 设备种类繁多从单板计算机到笔记本到平板硬件差异很大。部署 Madeira 之前先确认几个关键点。第一内核版本要足够新建议 5.15 以上否则 FEX-Emu 的一些特性可能不支持。第二图形驱动要完整特别是 Vulkan 驱动很多 ARM 设备的 Vulkan 驱动不完整会导致 DXMT 初始化失败。第三内存要足够建议至少 4GB2GB 的设备跑起来会很吃力。另外ARM 设备的散热往往不如 x86 设备长时间跑 Windows 应用会导致降频。可以试试限制 CPU 频率或者加个散热底座。电源管理也很重要有些设备在电池模式下会限制性能插电才能跑满。这些细节看起来不起眼但实际体验差别很大。6. 常见问题速查与避坑指南6.1 安装与配置阶段的高频问题安装阶段最常见的问题是依赖缺失。编译 FEX-Emu 时如果提示找不到libcap或libglib说明对应的开发包没装。不同发行版的包名不一样Debian 系是libcap-dev、libglib2.0-devRed Hat 系是libcap-devel、glib2-devel。建议先把官方文档里的依赖列表过一遍确保一个不漏。配置阶段最常见的问题是 prefix 冲突。如果你之前装过 Wine~/.wine目录可能已经存在直接覆盖会导致旧应用出问题。建议给 Madeira 单独建一个 prefix比如~/.wine-madeira并且用环境变量WINEPREFIX显式指定。另外WINEARCH一旦设定就不要改改了之后 prefix 里的 32 位和 64 位组件会混乱。6.2 运行阶段的性能与稳定性问题运行阶段的问题主要集中在性能和稳定性上。性能问题前面已经说了不少这里补充一点如果应用突然变慢先检查是不是后台有 Wine 的wineserver进程卡住了。可以用wineserver -k强制结束然后重新启动应用。稳定性问题方面如果应用频繁崩溃可以试试关闭 FEX-Emu 的 JIT 优化用解释模式跑虽然慢但稳定。解释模式通过FEX_INTERPRETER1环境变量启用。还有一个容易被忽略的问题是时区和区域设置。Wine 默认会读取系统时区但有些应用对时区敏感如果系统时区设置不对应用可能显示错误的时间或者直接崩溃。可以在winecfg里手动指定时区或者在环境变量里设置TZ。区域设置同理LANG和LC_ALL要设成应用支持的语言否则界面可能乱码。6.3 独家避坑技巧汇总第一个技巧是善用快照。在安装大型应用之前先给 Wine prefix 打个快照比如用tar打包整个目录。如果安装失败或者装完出问题直接恢复快照比卸载重装快得多。这个习惯能省下大量时间。第二个技巧是分离配置。不同的应用用不同的 prefix不要混在一起。虽然这样占磁盘空间但能避免 DLL 冲突和注册表污染。如果磁盘紧张可以用wineboot -u创建最小 prefix然后按需安装组件。第三个技巧是关注社区。Madeira 的社区虽然不大但活跃度还可以。遇到问题先搜社区历史帖大概率有人遇到过类似情况。另外 FEX-Emu 和 Wine 的官方仓库里issue 区也有很多有价值的讨论值得花时间翻一翻。注意不要随意混用不同来源的 Wine 和 DXMT 包。版本不匹配是很多诡异问题的根源统一从 Madeira 官方渠道获取能避免大量麻烦。7. 个人实操体会与后续折腾方向折腾 Madeira 这段时间最大的感受是兼容层技术已经比几年前成熟太多了但离“无感”还有距离。FEX-Emu 的指令翻译效率超出我预期Wine 的 API 覆盖度也够用DXMT 的图形转译在简单场景下表现不错。但三者叠加之后调试复杂度是指数级上升的。一个问题可能出在 FEX-Emu 的翻译层也可能出在 Wine 的 API 层还可能出在 DXMT 的图形层排查起来需要耐心和系统性的方法。我个人的经验是先把 FEX-Emu 单独调通确保它能正确翻译简单的 x86-64 程序然后再加 Wine确保 Windows API 调用正常最后再加 DXMT确保图形能渲染。分层调试比一上来就全链路跑要高效得多。另外日志一定要开虽然日志量大但关键时刻能救命。后续我打算试试把 Madeira 跑在更小的设备上比如树莓派或者类似的单板计算机看看极限在哪里。另外也想研究一下 FEX-Emu 的 JIT 优化策略看看有没有办法针对特定应用做定制优化。这个方向坑肯定不少但折腾本身就是乐趣所在。如果你也在玩类似的东西欢迎交流踩坑经验少走弯路总是好的。
返回列表