资讯动态

UEFI内存测试工具advmemtest:从算法到颗粒级错误定位

发布时间:2026/9/24 5:26:14 来源:尧图企业网站定制
1. 为什么我要自己写一个内存测试工具内存这东西平时不坏的时候你根本感觉不到它存在一旦出问题那真是能把人逼疯。蓝屏、随机重启、编译到一半编译器崩溃、游戏加载到某个固定进度就闪退、跑虚拟机的时候宿主机莫名其妙死机——这些症状你拿去搜十有八九会看到一句“先测内存”。问题是市面上能用的内存测试工具要么是闭源黑盒要么免费版限制轮数要么跑一晚上只告诉你“有错误”却不告诉你错在哪。我自己是做嵌入式和底层开发的平时接触的机器从工控机、迷你主机到工作站都有内存颗粒的品牌、批次、时序五花八门。有一次一批机器反复出问题MemTest86 跑了一整夜报了几个错但错误地址落在哪个颗粒、哪个 rank、哪个 bank 上完全不知道。你只能整条内存换掉成本高不说还未必是内存本身的问题——有可能是插槽、有可能是主板走线、有可能是 CPU 内存控制器。这种时候你就特别想要一个工具能把错误定位到具体的物理颗粒上。advmemtest就是我为了解决这个问题写出来的。它是一个基于 UEFI 环境运行的内存测试工具带图形界面免费版不限测试轮数Pro 版能把错误定位到内存颗粒级别。图形界面用的是 LVGL整个东西跑在 UEFI 里不依赖操作系统所以哪怕你的机器根本进不了 Windows只要还能进 UEFI 就能测。这篇文章我会把这个工具的来龙去脉、核心技术点、实操方法、踩过的坑全部讲清楚适合做硬件测试、整机维修、嵌入式开发、以及任何需要判断内存稳定性的朋友参考。2. 整体设计思路与关键技术选型2.1 为什么选择 UEFI 而不是传统 DOS 或 Linux做内存测试第一个要决定的就是运行环境。传统方案有三条路DOS 下的 MemTest86 老版本、Linux 下用 memtester、以及 UEFI 应用。DOS 方案的问题是内存寻址受限DOS 本身要占一部分常规内存测试大容量内存时需要通过扩展内存规范来访问而且 DOS 对现代硬件的支持很差很多新主板根本不提供 DOS 的 CSM 兼容模式你连启动都启动不了。Linux 方案的问题是它自己就要占用内存你测不了 Linux 内核占用的那部分区域而且 Linux 启动本身依赖内存正常工作如果内存坏得比较厉害Linux 可能根本起不来。UEFI 方案的优势在于它是固件层的东西在操作系统加载之前就运行可以访问几乎全部物理内存现代主板全部支持 UEFI 启动不存在兼容性问题UEFI 提供了标准的运行时服务和启动服务可以方便地做内存分配、定时器、图形输出。唯一的问题是 UEFI 环境下的开发资料相对少图形界面更是几乎没有现成方案这也是为什么大部分 UEFI 内存测试工具都是纯文本界面。我选 UEFI 还有一个实际原因很多出问题的机器恰恰是进不了系统的。客户拿过来一台机器说“开不了机”你插上 U 盘启动 advmemtest如果能进界面至少说明 CPU、基本内存、显卡输出是正常的这本身就是一个快速的分诊手段。2.2 图形界面为什么用 LVGL 而不是自己画UEFI 本身提供了 Graphics Output Protocol你可以直接往帧缓冲里写像素。理论上自己画界面也行但你要处理字体渲染、控件布局、事件响应、刷新优化工作量巨大而且做出来的东西很难看。LVGL 是一个轻量级嵌入式图形库原本是给单片机用的资源占用小移植性好。它支持按钮、标签、进度条、表格、Tab 控件这些常用控件正好满足内存测试工具的需求。把 LVGL 移植到 UEFI 上核心工作就是实现一个显示驱动和一个输入驱动显示驱动把 LVGL 的渲染结果刷到 GOP 的帧缓冲上输入驱动把 UEFI 的键盘和鼠标事件喂给 LVGL。这个组合的好处是界面开发效率极高。我可以用 LVGL 的 Tab 控件把界面分成几个页测试配置页、实时状态页、结果统计页、错误详情页。用户用键盘方向键或者鼠标就能切换进度条实时刷新错误列表滚动显示。相比纯文本界面图形界面的信息密度和可读性完全不是一个级别。2.3 测试算法怎么选从 March 到随机模式内存测试算法有很多种常见的有 March C-、March X、Moving Inversions、Random Pattern 等。不同算法针对的故障类型不一样有的擅长测固定为 0/1 的故障有的擅长测地址译码故障有的擅长测耦合故障。advmemtest 免费版内置了多套算法默认会跑一个组合先写全 0 读回校验再写全 1 读回校验然后跑 March C- 的变体最后跑随机数据模式。这样覆盖的故障类型比较全。每一轮测试会遍历所有可测试的内存区域轮数不限你可以让它一直跑到你手动停止。这里有个关键点测试区域的选择。UEFI 环境下固件本身、advmemtest 自己、以及一些保留区域是不能动的否则会直接死机。所以工具启动后第一件事是读取 UEFI 的内存映射表把可用内存区域标记出来只在这些区域里做测试。同时advmemtest 自己运行需要的内存也要从可用区域里划出来不能自己踩自己。2.4 Pro 版的颗粒定位是怎么实现的这是整个工具最核心的差异化功能。普通内存测试工具报错最多告诉你“地址 0x1A2B3C4D 处数据错误期望 0x55实际 0xAA”。但 0x1A2B3C4D 这个物理地址对应哪条内存、哪个 rank、哪个 bank、哪个颗粒它不告诉你。Pro 版的做法是通过 UEFI 的 SMBIOS 表读取内存设备的物理布局信息包括每条内存的起始地址、容量、rank 数、颗粒位宽。然后结合内存控制器的地址映射规则这个需要针对不同平台做适配Intel 和 AMD 的映射方式不一样把物理地址反推回具体的 rank、bank group、bank、row、column。最后根据颗粒的位宽和 rank 的组织方式算出这个地址落在哪个颗粒上。举个例子一条 8GB 的 DDR4 内存8 个颗粒每个颗粒 8Gb1GB位宽 8bit总位宽 64bit。如果错误地址落在某个 rank 的某个 bank 的某个 row 上通过地址解码就能算出是第几个颗粒。这样你就能精确知道是哪个颗粒坏了而不是整条内存换掉。这个功能的实现难度在于不同主板、不同内存控制器的地址映射规则不一样需要做大量的逆向和适配工作。目前 Pro 版支持主流 Intel 和 AMD 平台后续还在持续增加。3. 核心细节解析与实操要点3.1 UEFI 应用的开发环境搭建开发 UEFI 应用主流工具链是 EDK2。你需要准备Linux 或 Windows 开发机EDK2 源码可以从 GitHub 上的 tianocore/edk2 获取GCC 或 MSVC 编译器Python 用于构建脚本QEMU 用于模拟测试EDK2 的构建系统基于 DSC 和 INF 文件你需要为 advmemtest 写一个 INF 描述文件声明源文件、库依赖、编译选项。然后写一个 DSC 文件把 INF 包含进去指定输出格式为 X64 的 UEFI 应用。编译出来的东西是一个 .efi 文件放到 FAT32 格式的 U 盘根目录下的 EFI/BOOT/ 目录里命名为 BOOTX64.EFI就能从 UEFI 启动菜单里选择启动。或者你也可以在 UEFI Shell 里直接运行它。注意UEFI 应用的内存管理跟操作系统完全不一样。没有 malloc/free 的标准实现你要用 UEFI 的 Boot Services 提供的 AllocatePool 和 FreePool。而且这些服务在 ExitBootServices 之后就不可用了所以所有内存分配必须在调用 ExitBootServices 之前完成。3.2 LVGL 在 UEFI 上的移植要点LVGL 移植到 UEFI核心是实现两个回调显示刷新和输入读取。显示刷新方面UEFI 的 GOP 提供了一个线性帧缓冲你可以直接往里面写像素。LVGL 渲染完一帧后会调用你注册的 flush_cb你在这个回调里把 LVGL 的颜色缓冲区拷贝到帧缓冲的对应位置。注意像素格式要匹配GOP 常见的是 BGR 或 RGB 24 位/32 位格式LVGL 默认是 RGB565 或 ARGB8888需要做转换。输入方面UEFI 的 Simple Text Input Protocol 提供键盘事件Simple Pointer Protocol 提供鼠标事件。你需要在一个循环里轮询这些事件转换成 LVGL 的输入事件调用 lv_indev_read_cb 喂给 LVGL。LVGL 的时基也很重要。LVGL 需要一个毫秒级的时间源来驱动动画和任务调度。UEFI 的 Boot Services 提供了 GetTimer 相关的接口可以拿到高精度计时器用它来实现 lv_tick_inc 的调用。实操心得LVGL 默认的刷新周期是 30ms在 UEFI 环境下如果刷新太频繁会明显拖慢内存测试的速度。我的做法是把刷新周期调到 100ms只在测试进度有变化或者用户操作时才刷新这样对测试性能的影响可以忽略不计。3.3 内存测试算法的实现细节以 March C- 为例它的标准流程是从低地址到高地址写 0 读 0从低地址到高地址写 1 读 1从高地址到低地址读 1 写 0从高地址到低地址读 0 写 1从低地址到高地址读 1 写 0从低地址到高地址读 0 写 1每一步都要校验读回的数据是否等于期望值。如果不等记录错误地址、期望值、实际值。在 UEFI 环境下实现这些算法有几个坑第一编译器优化。你写*addr 0; if (*addr ! 0) error();这种代码编译器可能会觉得这个判断永远为真直接优化掉。解决办法是用 volatile 指针或者把读写操作封装成函数并加上编译器屏障。第二缓存影响。CPU 缓存会缓存内存访问你写进去的数据可能还在缓存里没落到内存颗粒上读回来的是缓存里的值测不出颗粒问题。解决办法是在测试前后做缓存刷新或者用非临时访问指令绕过缓存。UEFI 环境下可以用 AsmFlushCacheLine 或者直接操作 CR3 切换页表来刷新 TLB 和缓存。第三多核并发。现代 CPU 都是多核的如果你只在单核上跑测试其他核心可能在后台访问内存干扰测试结果。advmemtest 的做法是在测试开始前把其他核心都停住通过 UEFI 的 MP Services 发送 INIT-SIPI 或者直接让它们进入死循环只留一个核心跑测试。3.4 错误定位到颗粒的地址解码逻辑这部分是 Pro 版的核心我尽量讲清楚原理但不涉及具体平台的敏感细节。物理地址到颗粒的映射大致分三步第一步地址到 rank。内存控制器会把物理地址的高位用来选择 rank。比如双 rank 配置地址的某一位为 0 选 rank0为 1 选 rank1。具体是哪一位取决于内存控制器的交错粒度。第二步rank 内地址到 bank/row/column。DDR 内存的地址线是复用的RAS、CAS、WE 信号配合行地址和列地址。内存控制器会把物理地址的中间部分映射到 bank group、bank、row、column。这个映射规则每个平台都不一样需要查芯片手册或者做逆向。第三步column 和 bank 到颗粒。一条内存条上的多个颗粒是并联的它们共享地址线和控制线但数据线是分开的。所以同一个 column 地址会同时访问所有颗粒每个颗粒贡献自己位宽的数据。如果错误数据的某一位出错就能定位到对应的颗粒。注意这个定位逻辑依赖准确的内存拓扑信息。如果 SMBIOS 表里的信息不准确或者主板厂商没有正确填写定位就会出错。我在实际使用中发现品牌整机的 SMBIOS 信息通常比较准确DIY 主板有时候会偷懒。遇到这种情况Pro 版会提示用户手动确认内存配置。4. 完整实操流程与核心环节实现4.1 制作启动 U 盘你需要一个 FAT32 格式的 U 盘容量不限128MB 都够用。把编译好的 advmemtest.efi 放到 U 盘的 EFI/BOOT/ 目录下重命名为 BOOTX64.EFI。如果你的 U 盘已经是 FAT32 且没有 EFI 目录手动建一个就行。然后重启目标机器进入 UEFI 启动菜单。不同品牌的机器进入方式不一样联想通常是 F12戴尔是 F12惠普是 F9华硕是 F8微星是 F11。如果按了没反应可能需要先在 BIOS 里关闭快速启动或者开启 UEFI 启动模式。实操心得有些机器默认只从内置硬盘启动U 盘启动选项被隐藏了。这时候你需要进 BIOS 设置在启动顺序里把 USB 设备调到第一位或者临时禁用安全启动。安全启动会阻止未签名的 UEFI 应用运行advmemtest 免费版没有签名所以需要关闭安全启动。Pro 版提供了签名版本可以兼容安全启动。4.2 界面操作与测试配置启动后你会看到 LVGL 画的界面顶部是 Tab 栏有“配置”“状态”“结果”“关于”四个页。配置页里可以设置测试模式快速单轮 March C-、标准多算法组合、压力随机模式无限循环测试范围全部可用内存、指定地址范围、指定大小错误处理遇到错误继续测试、遇到错误暂停、遇到错误停止刷新频率界面刷新间隔默认 100ms状态页显示当前测试进度、已用时间、已测轮数、当前算法、实时错误计数。进度条会随着测试推进而增长每完成一轮会重置。结果页显示错误列表每条错误包含地址、期望值、实际值、所属算法、轮数。Pro 版还会显示颗粒编号。关于页显示版本信息、支持的平台列表、以及一些快捷键说明。4.3 测试执行与结果解读配置好之后点“开始”工具就开始跑测试。免费版不限轮数你可以让它跑一晚上。第二天早上看结果页如果有错误记录下错误地址和数量。这里要强调一个判断标准偶尔一个错误和大量错误意义完全不同。如果跑了一整夜只报了一两个错误有可能是宇宙射线或者电源波动导致的软错误不一定是内存坏了。如果错误成片出现或者同一个地址反复报错那基本可以确定是硬件问题。Pro 版的颗粒定位会告诉你错误集中在哪个颗粒。如果多个错误都落在同一个颗粒上那换掉那个颗粒如果是可更换的颗粒或者整条内存就行了。如果错误分散在多个颗粒上可能是内存控制器或者主板走线的问题。注意测试过程中机器会发热特别是内存和 CPU。如果散热不好高温可能导致误报。建议在通风良好的环境下测试或者监控温度。advmemtest 本身不读温度传感器但你可以用其他工具配合监控。4.4 参数计算测试时间估算内存测试的时间跟容量、算法复杂度、内存带宽都有关系。以 16GB 内存、March C- 算法为例March C- 需要遍历内存 6 次每次读写各一遍总共 12 次内存访问。16GB 就是 16×1024×1024×1024 字节乘以 12 次访问再除以内存带宽。假设内存带宽是 20GB/s那么理论时间是 16×12/20 ≈ 9.6 秒。但实际上 UEFI 环境下的内存访问效率远低于操作系统因为缓存没开、预取没开、多核没利用。实测下来16GB 跑一轮 March C- 大概需要 3 到 5 分钟。如果跑标准模式多算法组合一轮大概 15 到 20 分钟。所以如果你要跑一晚上大概能跑 20 到 30 轮标准模式。这个轮数对于发现间歇性故障已经足够了。5. 常见问题与排查技巧实录5.1 启动失败黑屏、卡死、自动重启这是最常见的问题。可能的原因和排查方法现象可能原因排查方法选择 U 盘启动后黑屏安全启动未关闭进 BIOS 关闭 Secure Boot启动后卡在厂商 LogoU 盘格式不对确认 U 盘是 FAT32文件路径正确启动后自动重启内存故障严重换一台机器测试 U 盘是否正常提示“不是有效的 UEFI 应用”编译架构不对确认编译的是 X64 架构进入界面但鼠标键盘无响应输入协议不支持尝试用键盘操作或换 USB 2.0 接口实操心得有些机器的 UEFI 实现有 bug对非签名应用支持不好。我遇到过一台联想机器必须把 U 盘插在特定的 USB 接口上才能启动。还有一台戴尔机器需要在 BIOS 里把“USB 启动支持”从“仅限启动盘”改成“所有 USB 设备”。5.2 测试过程中死机或重启如果测试跑到一半死机大概率是测到了坏的内存区域而且坏得很严重导致工具本身崩溃。这时候你可以缩小测试范围避开已知的坏区域降低测试强度比如只用简单的全 0/全 1 模式如果一测就死那内存基本可以判死刑了还有一种可能是工具本身的 bug。advmemtest 在测试时会保护自己运行所需的内存区域但如果内存映射表有问题可能会踩到不该踩的地方。遇到这种情况可以在配置页里手动排除一段地址范围。5.3 错误地址无法定位到颗粒Pro 版的颗粒定位依赖 SMBIOS 信息和平台适配。如果遇到无法定位的情况检查 SMBIOS 信息是否完整可以在 UEFI Shell 里用 smbiosview 命令查看确认平台是否在支持列表里目前支持 Intel 6 代到 13 代酷睿、AMD 锐龙 1000 到 7000 系列如果是服务器平台可能需要额外的适配注意颗粒定位不是万能的。如果错误发生在地址译码电路或者内存控制器的缓存里可能无法对应到具体颗粒。这种情况下错误地址会显示为“未知”。5.4 测试速度太慢UEFI 环境下的内存测试速度受限于几个因素单核运行为了准确性只用一个核心跑测试缓存未优化UEFI 不开启 CPU 缓存预取界面刷新LVGL 刷新会占用 CPU 时间如果觉得太慢可以把界面刷新频率调到 500ms 或更低选择快速模式只跑 March C-缩小测试范围只测怀疑有问题的区域但要注意降低测试强度可能会漏掉一些间歇性故障。我的建议是第一次测用标准模式跑几轮如果没问题再考虑加速。5.5 与其他内存测试工具的结果不一致不同工具的测试算法、测试范围、错误判定标准都不一样结果不一致是正常的。比如 MemTest86 会测试一些 advmemtest 不测的保留区域而 advmemtest 的随机模式可能测出 MemTest86 漏掉的故障。如果两个工具结果矛盾我的建议是以报错的工具为准宁可信其有用第三个工具交叉验证如果只有一个工具报错而且错误很少可以先观察不急着换内存6. 后续扩展与个人体会这个工具我还在持续迭代。接下来想做的几个方向一是增加对 DDR5 平台的支持DDR5 的地址映射和 DDR4 差别很大需要重新适配二是增加温度监控把内存温度和错误率关联起来三是做一个网络版可以通过网络远程查看测试进度适合批量测试的场景。LVGL 这边也有优化空间。目前界面还比较朴素后续想加一些图表比如错误率随时间的变化曲线、不同算法的错误分布对比。LVGL 的图表控件已经比较成熟了移植过来应该不难。最后分享一个我在实际使用中总结的小技巧如果你怀疑某条内存有问题但测试又跑不出错误可以试试把内存条换个插槽再测。有时候问题不在内存本身而在插槽或者主板走线。我遇到过一条内存在 A2 插槽上跑测试必错换到 B2 插槽就完全正常最后发现是 A2 插槽有一根针脚接触不良。这种问题再好的测试工具也测不出来只能靠换插槽来排查。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价