
最近把手里一块吃灰多年的 ARM 开发板翻了出来打算让它重新上岗。真正动手之后发现最大的拦路虎不是CPU性能也不是内存容量而是那颗用了好几年的 8GB eMMC Flash——空间捉襟见肘跑完系统加几个应用就剩不下多少了。索性做一次彻底升级把存储扩容连同系统一起重新部署顺便把交叉编译环境也捋顺。这篇文章就把这次完整过程记录下来从硬件选型、拆焊备份、系统烧录到工具链配置和问题排查一步步拆开讲清楚给同样在折腾 ARM 板子的朋友做个参考。这篇内容适合正在捣鼓 ARM 开发板、准备给板子扩容或者迁移系统的嵌入式爱好者也适合刚接触 ARM 交叉编译、想弄明白整条工具链怎么串起来的新手。我会把涉及的关键原理讲明白再把可以直接抄作业的命令和步骤放出来看完你不仅能复现这次eMMC升级还能少走不少弯路。1. 重新认识 ARM 开发板上的 eMMC Flash1.1 eMMC 到底是什么为什么 ARM 板子都在用eMMC 的全称是 embedded MultiMediaCard翻译过来就是嵌入式多媒体存储卡。它本质上把 NAND Flash 闪存颗粒和一个控制芯片封装在同一个 BGA 封装里对外提供标准的 MMCA 接口协议操作系统只需要通过 SD/MMC 控制器就能读写不需要关心底层 NAND 的坏块管理、ECC 校验、磨损均衡这些复杂逻辑。这个设计思路很像把一整套存储管理方案做成一个黑盒子SoC 这边省事很多。ARM 开发板偏爱 eMMC 而不是裸 NAND Flash原因其实很现实。裸 NAND 需要 SoC 端自己实现 FTLFlash Translation Layer算法处理坏块、磨损均衡、垃圾回收这套东西做起来工作量不小而且一旦算法有 bug文件系统里的数据可能悄悄损坏。eMMC 把这些问题都封装在内部控制器里SoC 只需要跑标准的块设备驱动稳定性大幅提升。再加上 eMMC 封装体积小、焊盘标准化量产时贴片方便所以从树莓派到瑞芯微、全志、晶晨的方案几乎清一色用 eMMC 或者 SD 卡作为启动介质。我手上这块板子用的是 8GB eMMC芯片型号是三星 KLMBG4JETD-B041属于 eMMC 5.0 规格理论读取速度大概 150MB/s 左右。这个性能在当年够用放到现在跑个带桌面环境的 Linux 发行版开机和应用程序加载都能明显感觉到瓶颈。1.2 8GB 容量用在 ARM 板上到底够不够不少朋友觉得 8GB 听起来不小手机都 128GB 起步了怎么开发板 8GB 还不够用。这里面的差异在于使用场景完全不同。手机的系统镜像经过高度优化用户数据大部分在云端本地存储主要是缓存。但 ARM 开发板很多时候要跑完整的 Linux 系统包含内核、根文件系统、开发调试工具、交叉编译产物还要装各种库。我给这块板子装的是基于 Debian 的发行版光根文件系统就占掉 2.5GB 左右再装个 Python 运行环境、几个常用开发库、Node.js、编译工具链轻松突破 4GB。剩下几个 GB 看着多放几个交叉编译出来的测试程序、跑点数据采集任务很快就见底。更要命的是eMMC 接近满容量时垃圾回收机制会频繁触发写入放大效应加剧性能暴跌板子会变得明显卡顿。所以这次升级的核心思路很明确把主板上的 8GB eMMC 换掉换成 32GB 甚至更大容量的芯片从根上解决容量焦虑。升级过程中涉及拆焊、系统迁移、交叉编译这几个关键环节下面逐个展开讲。2. 升级前的硬件分析与方案选择2.1 先看清板子的存储拓扑动手之前千万别急着下单买芯片先把自己板子的存储拓扑摸清楚。同样是 ARM 开发板启动方式差别很大有的板子从 eMMC 启动有的从 SD 卡启动还有的支持 SPI NOR Flash 和 eMMC 双启动。启动顺序由 SoC 内部的 BootROM 决定一般会先检测特定 GPIO 电平配置再按照内部固定的优先级去初始化启动设备。我一般按这几个步骤确认板子的存储架构查看 SoC 芯片手册里 Boot Device 章节确认是否支持 eMMC 启动以及启动优先级看原理图确认 eMMC 芯片的供电、CLK、CMD、DATA0~DATA7 的连接在串口终端进入 U-Boot执行mmc list和mmc info命令查看识别的存储设备注意不同厂商的 SoC 对 eMMC 的初始化时序要求不一样选择替换芯片时尽量选同一代、同规格甚至同厂商的型号避免因为 eMMC 初始化参数差异导致无法识别。2.2 三种扩容方案怎么选扩容不一定非要拆焊有三种主流思路我按推荐程度简单聊聊。方案一直接替换 eMMC 芯片。用热风枪把旧的 8GB 芯片吹下来重新植锡换上大容量芯片。优点是一劳永逸系统从内部存储启动性能和稳定性最好缺点是需要焊接功底BGA 封装拆装有一定风险新手操作不当可能焊盘脱落直接报废板子。方案二SD 卡启动。很多 ARM 板的 SoC 支持直接从 SD 卡启动只要在 U-Boot 里把启动设备切到 SD 卡就行。优点是不用动烙铁成本极低随便找张高速 TF 卡就能跑缺点是 SD 卡的可靠性不如 eMMC长时间通电运行容易掉数据而且 IO 性能受限于 SD 卡本身质量读写速度不稳定。方案三USB 外接存储启动或扩展。某些板子支持从 USB 硬盘/U盘启动系统或者把数据分区挂到外部存储上。优点是完全不影响板子原有结构缺点是系统本身还在 eMMC 里只是外部扩展容量治标不治本。综合评估下来我选了方案一。这块板子的 eMMC 封装是 BGA153焊接难度在可接受范围内而且我要跑一些对 IO 性能有要求的任务优先保证稳定性。2.3 新 eMMC 芯片的选型细节选定方案后芯片选型也是个值得说道的事情。先说结论我更推荐选择知名原厂的 eMMC比如三星、闪迪、海力士、美光这些品牌而不是某些价格很低的兼容片。原因有两个一是原厂芯片的坏块管理算法更成熟长期运行不容易出现掉速二是在 U-Boot 和内核里的兼容性更好出现识别问题的概率低。具体参数上需要注意三点封装规格必须和主板焊盘匹配。常见的有 BGA153、BGA100、BGA169引脚间距不一样硬装是装不上去的。我确认了原板是 BGA153新芯片也买同样的封装。eMMC 协议版本尽量选和板子 SoC 支持的版本匹配。比如 SoC 只支持 eMMC 5.0你买 eMMC 5.1 的芯片也能用协议是向后兼容的但无法发挥新版本的极限性能反过来如果 SoC 比较新支持更高版本你却买个老标准芯片等于降低性能上限。工作温度等级消费级 eMMC 工作温度一般是 -25℃~85℃工业级是 -40℃~85℃。如果板子要放在户外或者高温环境选工业级更稳妥价格也会贵一些。我最终选了一块 32GB 的三星 eMMC 5.1 芯片单从容量和协议版本上看都是升级。这里还要插一句买芯片的时候尽量让卖家提供出厂测试报告确认是全新的而不是拆机片不然跑一段时间出现坏块就麻烦大了。3. 备份原系统与搭建交叉编译环境3.1 先把原厂镜像完整备份出来升级前最重要的一步是把原 eMMC 里的内容完整备份无论后面是直接迁移还是重新做系统手里有原厂镜像心里都不慌。备份有两种常用方式一种是操作系统运行时直接用dd命令整块读取另一种是进 U-Boot 通过网络把 eMMC 内容传出来。第一种方式最直接。板子上电启动到 Linux确认 eMMC 对应的设备节点。执行lsblk能看到类似/dev/mmcblk0的设备对应的分区一般是/dev/mmcblk0p1、/dev/mmcblk0p2这种。把整块 eMMC 备份到一个文件里命令如下sudo dd if/dev/mmcblk0 of/home/user/backup/emmc8g_backup.img bs4M statusprogress convsync,noerror注意bs4M可以显著提升读取速度convsync,noerror保证读取过程中遇到坏块不会中断而是填充空数据继续跑。备份出来的镜像大小会接近 8GB建议放到外接存储或者通过网络传到 PC 上。第二种方式适合系统启动不了的情况。在 U-Boot 命令行里先用mmc dev 0选中 eMMC 设备然后通过tftp或者fastboot协议把内容传到 PC。不过这种方式操作相对繁琐需要先搭建 TFTP 服务器还要设置好网络参数我这次系统能正常启动就直接用dd搞定了。3.2 安装交叉编译工具链备份结束后我决定不直接迁移原系统而是趁这次升级重新编一版干净的根文件系统顺便把交叉编译环境彻底跑通。交叉编译的意思是在 PC 上编译生成 ARM 架构下可运行的二进制文件而不是在 ARM 板上本地编译。这样做的好处是编译速度快、效率高毕竟开发板的性能摆在那里编译一个稍微大点的项目可能要等半天。不同类型的 ARM 板需要不同的工具链前缀我整理了一个简单的对应关系目标平台工具链前缀典型适用场景armv7 32位arm-linux-gnueabihf-Cortex-A7/A9 等老款 32 位核心armv8 64位aarch64-linux-gnu-Cortex-A53/A72 等 64 位核心裸机环境arm-none-eabi-单片机、无操作系统的固件开发先确认板子的架构。我的板子 CPU 是 64 位的所以用 aarch64 工具链。在 PC 上安装sudo apt update sudo apt install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu装完验证一下工具链是否可用aarch64-linux-gnu-gcc --version aarch64-linux-gnu-gcc -v这里有个小坑很多新手直接执行gcc而不是aarch64-linux-gnu-gcc编译出来的还是 x86 架构的程序放到 ARM 板上会直接报Exec format error。这个问题后面问题排查部分会再展开。3.3 用 BusyBox 做最小根文件系统拿到工具链之后我决定用 BusyBox 编译一个最小根文件系统用来验证新 eMMC 启动链路是否正常。BusyBox 是一个集成了数百个常用 UNIX 命令的精简可执行文件做嵌入式系统的标配。先下载 BusyBox 源码然后配置交叉编译参数wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2 tar xjf busybox-1.36.1.tar.bz2 cd busybox-1.36.1 make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- menuconfig在菜单里进入 Settings勾选 Build static binaryno shared libs这样编译出来的 BusyBox 是静态链接的不依赖任何动态库放到任何 rootfs 环境都能直接运行省去拷贝一堆 .so 的麻烦。设置完保存退出执行make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- -j$(nproc) make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- install安装完成后会有_install目录里面是 bin、sbin、usr 这些基本目录结构。再手工补几个必要的目录和文件cd _install mkdir -p proc sys dev etc touch etc/inittab这样一个最小的可启动 rootfs 骨架就搭出来了。后续如果想跑完整功能直接把 Debian/Ubuntu 的 rootfs 用 debootstrap 做出来再把 BusyBox 替换成完整命令集即可。3.4 用 QEMU 先做一轮仿真验证交叉编译出来的 rootfs 不用急着烧到板子上可以先在 PC 上用 QEMU 仿真的方式验证一遍。QEMU 支持模拟多种 ARM 开发板通过-M参数可以指定板型比如-M vexpress-a9模拟 ARM 32 位开发板-M virt模拟 ARM 64 位虚拟平台。我用来验证的 64 位环境命令大概长这样qemu-system-aarch64 -M virt -cpu cortex-a53 -smp 2 -m 1G \ -kernel Image -append root/dev/vda rw consolettyAMA0 \ -drive file/path/to/rootfs.img,formatraw,ifnone,idhd0 \ -device virtio-blk-device,drivehd0 -nographic这里需要先准备一个内核镜像Image可以从发行版仓库里拿也可以自己用make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- defconfig编译。QEMU 的好处是调试起来非常灵活崩了直接重启虚拟机就行不用反复烧写开发板。我这次的 rootfs 就是在 QEMU 里确认能正常启动、能执行基本命令之后才进入下一步烧录。4. 拆焊换芯与系统烧录部署4.1 拆焊 eMMC 的操作细节硬件操作是整个升级过程中风险最高的一步。BGA 封装的 eMMC 芯片底部有密密麻麻的锡球拆的时候要控制好温度和时间否则容易把焊盘带下来或者损伤周边元器件。我用的工具是热风枪、助焊剂、撬片、钢网和植锡台。操作流程梳理如下给主板拆掉所有排线和易损部件用耐高温胶带保护 eMMC 芯片周边的元件尤其是晶振、电容这些怕高温的小零件。在 eMMC 芯片四周均匀涂抹助焊剂方便热传导和锡球熔化。热风枪温度设置在 340℃~360℃ 之间风速调至中低档。风嘴对准芯片做画圈式均匀加热大约 30~45 秒后用镊子轻轻试探芯片边缘如果能轻微推动说明锡球已经熔化再用撬片小心地撬起芯片。拆下芯片后用烙铁配合吸锡带清理焊盘上的残留焊锡确保焊盘平整干净。给新 eMMC 芯片植锡。放上对应尺寸的钢网刮上锡膏用热风枪吹成均匀的锡球。注意千万不要在锡球尚未完全熔化时就用力撬芯片强行撬会直接把焊盘扯下一个角这种损伤往往修不回来。判断锡球是否熔化靠的是芯片能否被轻轻推动而不是靠蛮力。植好锡后把新 eMMC 对准焊盘方向放上去注意芯片上的圆点标记要和 PCB 上的丝印圈一致。再次加热等芯片自动下沉贴合焊盘用镊子轻轻碰一下芯片确认已经固定就完成了焊接。4.2 烧录系统镜像到新 eMMC焊接完成后先别急着装系统先用万用表确认几个关键电源引脚没有短路再上电看串口日志。如果能进 U-Boot说明硬件焊接成功。接下来面临一个新的问题新 eMMC 是空白的里面没有引导程序怎么让它启动到系统不同的 SoC 方案有不同的烧录方式瑞芯微方案按住 recovery 键上电PC 上用 RKDevTool 烧录全志方案按住 FEL 键上电PC 上用 PhoenixSuit 烧录树莓派方案通过 SD 卡启动后把 eMMC 挂载为 USB 存储设备再烧录通用方案通过 U-Boot 的网络功能TFTP/NFS加载镜像后写入 eMMC我手里这块板子用的是全志方案所以烧录用 PhoenixSuit。把板子切换到 FEL 模式连接 USB 线PC 上打开烧录工具选择打包好的系统镜像点升级即可。这个过程本质上是 PC 通过 USB 把引导程序、内核、rootfs 依次写入 eMMC 的指定位置。如果你在 Linux 环境下做烧录也可以直接用命令行工具比如 sunxi-fel 配合 livemedia 模式操作灵活度更高。不过 PhoenixSuit 的图形界面更直观适合第一次操作的人。4.3 分区规划与文件系统布局烧录工具只是把预先打包好的镜像原样灌进去真正决定系统怎么组织存储的是分区表。我这次利用 32GB 的空间重新规划了分区分区大小挂载点用途/dev/mmcblk0p1128MB/boot存放内核和 DTB 文件/dev/mmcblk0p220GB/根文件系统/dev/mmcblk0p3剩余空间/data用户应用与日志数据先用fdisk在板子上对/dev/mmcblk0重新分区然后格式化文件系统sudo fdisk /dev/mmcblk0 # 依次删除旧分区、创建新分区按上述规划设置大小 sudo mkfs.vfat /dev/mmcblk0p1 sudo mkfs.ext4 /dev/mmcblk0p2 sudo mkfs.ext4 /dev/mmcblk0p3分区完成后把 rootfs 解压到根分区挂载 boot 分区拷贝内核sudo mount /dev/mmcblk0p2 /mnt sudo tar xjf rootfs.tar.bz2 -C /mnt sudo mount /dev/mmcblk0p1 /mnt/boot sudo cp Image /mnt/boot/ sudo umount /mnt/boot /mnt分区规划之所以要把数据目录单独拉出来是因为系统升级时只需要覆盖根分区用户数据不会丢失这个习惯在嵌入式设备上尤其重要。5. 性能测试与稳定性验证5.1 用 dd 和 fio 测真实读写速度系统跑起来之后先别着急装各种软件第一时间做一轮性能测试确认新 eMMC 没有虚标、接口时序也正常。最快速的验证方法是dd命令虽然它测的是顺序读写但已经能反应大方向# 测写入速度 sudo dd if/dev/zero of/tmp/testfile bs1M count1024 convfdatasync # 测读取速度先清理缓存 sudo sh -c sync echo 3 /proc/sys/vm/drop_caches sudo dd if/tmp/testfile of/dev/null bs1M count1024实际跑下来我的新 eMMC 顺序写大约 80MB/s顺序读大约 145MB/s对比原来那颗只有几十 MB/s 的水平提升明显。更深层的测试建议用fio它能模拟随机读写、混合读写、队列深度等真实负载。安装完 fio 后按下面这个参数跑一轮sudo fio --namerandwrite --ioenginelibaio --rwrandwrite --bs4k \ --direct1 --size512M --numjobs1 --runtime60 --group_reporting--direct1表示绕过系统缓存直接写设备测出来的才是 eMMC 的真实随机写性能。4K 随机写一般都在几十 MB/s 以内但如果掉到个位数就要考虑是不是芯片本身有问题或者接口配到了低速模式。5.2 长期稳定性与掉电保护测试性能只是第一关嵌入式设备更怕的是长期运行中出现数据损坏。eMMC 芯片虽然内部有磨损均衡机制但如果写入负载过大、温度过高还是可能出现意外。我做稳定性验证时走了两步。第一步是压力跑分让 fio 在后台跑 2 小时高负载随机写同时用sensors观察芯片温度。eMMC 正常工作温度最好压在 70℃ 以内超过这个值就需要考虑加散热片。第二步是掉电测试在持续写文件的过程中随机断电再重启检查文件系统能否自动恢复、关键配置文件有没有损坏。这个测试不建议用正在跑业务的板子做专门拿一块测试板折腾就行。还有一个很容易被忽略的点就是更新完 eMMC 后U-Boot 的环境变量里可能还记录着旧的启动参数比如bootcmd或者mmcroot指向错误的分区。我这次就遇到了系统卡在内核挂载根文件系统的阶段排查了半天发现是 U-Boot 的bootargs里根分区参数还是旧的。遇到这种问题在 U-Boot 命令行里改过来再保存即可setenv bootargs consolettyS0,115200 root/dev/mmcblk0p2 rw rootwait saveenv boot6. 升级过程中的高频问题与排查技巧6.1 新 eMMC 识别不到焊接完成后上电串口完全没输出或者 U-Boot 里执行mmc list看不到设备。这种问题八成出在硬件接触上。先用放大镜检查芯片引脚有没有连锡、虚焊再用万用表测量关键引脚对地阻值是否正常。很多时候是新芯片植锡的时候锡球大小不均导致部分引脚没有完全接触焊盘。如果引脚没问题再确认供电。eMMC 通常有独立的 VCC3.3V和 VCCQ1.8V/3.3V供电用万用表测芯片旁边的电容两端电压是否正常。曾经碰到过焊接时热风枪温度太高把旁边的电源 LDO 吹出问题的案例排查起来非常费劲。6.2 烧录完成后启动卡在 U-BootU-Boot 启动正常但到了加载内核阶段卡住不动。优先检查 boot 分区里的内核镜像和 DTB 文件是否存在以及文件名是否和 U-Boot 环境变量里设置的一致。如果 U-Boot 是按fatload mmc 0 0x60000000 Image这种命令加载的而 boot 分区里确实没有Image文件U-Boot 会提示找不到文件或者直接卡在等待。另一个隐藏很深的原因是 DTB 里配置的内存地址和实际硬件不匹配特别是换了 eMMC 后引导顺序变化可能导致某些外设初始化异常。解决方式是先在 U-Boot 里跑printenv查看启动过程到底卡在哪一步再对症处理。6.3 内核报 rootfs 挂载失败卡在VFS: Cannot open root device mmcblk0p2这类错误是 rootfs 挂载失败。先用lsblk确认分区表是否正确写入再确认 U-Boot 传给内核的root参数和实际 rootfs 所在分区一致。如果用了 ext4 文件系统别忘了在内核配置里打开对应的文件系统驱动否则内核根本不认识 ext4 格式。还有一种情况是 rootfs 目录权限不对。用 BusyBox 做的最小 rootfs 如果/init没有可执行权限或者文件属于错误的所有者内核同样无法完成用户态初始化。遇到这种问题在板子上挂载 rootfs 后检查/init和/bin/busybox的权限是否都有x标志。6.4 交叉编译程序运行时报 No such file or directory这个问题特别典型。在 PC 上交叉编译好的可执行文件拷贝到 ARM 板子上执行时却提示No such file or directory而文件明明就在那里。很多人第一反应是文件没传完整其实大概率是链接器找不对。如果编译时没有使用静态链接可执行文件里记录了解释器的路径一般是/lib/ld-linux-aarch64.so.1这种。板子的 rootfs 里如果缺少这个动态链接器或者编译器默认的路径和板子上的实际路径不一致内核在加载程序时就会报找不到解释器。解决方法是编译时加上aarch64-linux-gnu-gcc -o hello hello.c -static或者用-Wl,--dynamic-linker指定正确的解释器路径。用readelf -l hello | grep interpreter可以快速查看当前程序依赖的解释器路径直接在板子上对比一下就能定位问题。7. 几点实操心得和后续扩展这次升级从头到尾走了一遍最大的体会是嵌入式开发很多事情看着简单实际操作起来细节决定成败。拆焊 eMMC 看起来就是加热、撬起、焊上三个动作但温度曲线、保护措施不到位板子就可能报废系统烧录看着就是点几个按钮但分区规划不合理后面扩容、升级都会很痛苦。根据我的经验再分享几个小技巧。准备一个优质的可调温热风枪和对应型号的钢网这类工具别省钱差的钢网植锡时锡球大小不均匀很容易虚焊。系统备份不只是升级前要做日常做好定期备份更重要尤其是准备改动内核参数或者换软件源之前先镜像一份当前系统出问题还能快速回滚。这块板子后续我打算接着折腾的方向也有不少。32GB 的 eMMC 空间富余出来了可以尝试在上面跑容器化的应用部署一套基于 Docker 的物联网边缘网关也可以利用空闲分区搞一个 NFS 共享或者内网同步盘。还有一点是值得花时间研究的调优 eMMC 的 IO 调度策略把系统默认的 CFQ 换成 deadline 或 noop在某些场景下能明显降低读写延迟这又是一个可以深入聊的话题。