ARTICLE DETAIL

资讯详情

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

嵌入式Linux实战:基于Hi3559AV100制作ext4根文件系统并烧录eMMC

嵌入式Linux实战:基于Hi3559AV100制作ext4根文件系统并烧录eMMC 做嵌入式Linux开发最绕不开的一关就是给目标板做根文件系统再把它烧进eMMC。Hi3559AV100这颗芯片算力强、外设丰富常用于智能视频和边缘计算项目但配套的SDK和文档默认引导你用NFS或者SD卡调试真正要到量产阶段还是得把整个系统塞进eMMC里稳定启动。这篇文章我把基于Hi3559AV100制作ext4根文件系统、打包镜像、烧录eMMC的全过程完整过一遍包括踩过的坑和排查思路给正在做同类平台的朋友一份可以直接抄作业的参考。整个流程的核心链路是手工构建rootfs目录结构 - 用busybox准备基础用户空间 - 从工具链拷贝glibc动态库 - 补齐设备节点和启动脚本 - 用mkfs.ext4打包成镜像 - 通过uboot或SD卡方式写入eMMC分区 - 配置内核启动参数。这套方法不依赖SDK里自带的打包脚本每一步都能自己控制排错也方便适合需要深度定制系统的项目。1. 项目概述与方案选型1.1 为什么要在Hi3559AV100上自己制作根文件系统Hi3559AV100是海思推出的智能视频处理器双核ARM Cortex-A73和双核Cortex-A53的big.LITTLE架构集成NNIE神经网络加速单元、IVE智能视觉引擎最高支持8K30帧视频编码。从我实际做的项目来看这种级别的SoC跑Linux系统是完全够格的CPU、内存、存储接口都按工业级标准设计不像MCU那样需要考虑资源天花板。SDK里其实提供了编译好的rootfs但直接用有三个问题第一个SDK自带的根文件系统通常包含大量用不到的测试程序、调试工具和库文件镜像我见过动辄几百MB烧录慢、占用空间、还有安全风险第二个有些板子的SDK版本只提供jffs2或squashfs镜像不适合需要读写日志和配置的场景第三个也是最关键的你想在系统里预置自己的应用程序、服务脚本、环境变量手工构建才能完全掌控。所以我在这类项目上从来不用SDK的现成文件系统都是自己从头搭。1.2 文件系统选择ext4 为什么是eMMC场景的首选这年头文件系统选择确实多Ubifs、jffs2、squashfs、f2fs、ext4、xfs各有各的适用场景。但在Hi3559AV100这类平台的eMMC存储方案里我基本固定用ext4原因很实际。eMMC和NAND Flash不一样它本身内部有FTL层和坏块管理对外暴露的是标准的块设备接口逻辑上更接近机械硬盘不像裸NAND那样需要在文件系统层做磨损均衡。因此专为裸NAND设计的jffs2、ubifs在eMMC上完全没有必要——它们带来的overhead不小性能还不一定比ext4好。ext4是Linux内核原生支持最完善的文件系统之一日志机制成熟、掉电恢复能力可靠、支持扩展属性、支持在线扩容工具链e2fsprogs稳定到不能再稳定。f2fs虽然是专门为Flash设计的日志型文件系统理论上有优势但在eMMC这种自带FTL的介质上实际提升有限而且遇到问题网上参考资料少不太适合工业量产项目。xfs性能确实好尤其适合大文件和大分区场景可嵌入式系统的kernel配置项会更重一些没必要为了用不上多少的大文件吞吐能力付出额外复杂度和内存占用。所以结论很简单eMMC ext4是兼容性、稳定性、工具链成熟度综合起来最让人省心的组合。1.3 eMMC烧录的整体思路在做具体操作之前先把烧录思路理清楚。Hi3559AV100支持从多种介质启动可以配置为从eMMC启动也可以从SD卡启动。烧录这件事本质上就是按照分区表把各个镜像写到eMMC对应的分区偏移地址上。常见的三种烧录路径一是通过HiTool网口烧写需要先让设备进入uboot烧录模式HiTool会把fastboot、内核、dtb和文件系统依次写入eMMC分区适合单板调试阶段二是uboot里直接用tftp下载镜像到内存然后使用mmc write命令写入eMMC这种方式灵活不用依赖上位机工具我看很多产线就是这么干的三是先从SD卡启动一个最小Linux环境再在Linux环境下把ext4镜像通过dd命令写入eMMC适合批量烧录和恢复变砖的板子。无论哪种方式分区表必须一致否则内核启动时传的root分区号和实际烧录位置对不上就启动失败。这个后面4.5节会重点说。2. 制作ext4根文件系统的完整流程2.1 准备交叉编译工具链与基础目录结构工欲善其事必先利其器。Hi3559AV100 SDK自带的工具链通常叫arm-himix100-linux路径一般在SDK的osdrv/arm-himix100-linux目录下。如果SDK已经装好了直接在/etc/profile里加上工具链路径或者每次编译前手动export。export PATH/opt/hisi-linux/x86-arm/arm-himix100-linux/bin:$PATH arm-himix100-linux-gcc -v确认工具链可以正常工作后先规划rootfs目录。我习惯在工作目录建一个rootfs文件夹里面按FHS标准创建子目录。mkdir -p rootfs/{bin,sbin,usr/bin,usr/sbin,usr/lib,etc,lib,dev,proc,sys,tmp,var,home,root,mnt,opt} chmod 755 rootfs为什么目录结构这么重要因为Linux启动后的第一个用户进程init通常指向busybox会依赖这些标准路径去找配置文件和动态库。缺一个目录可能某个程序启动时就会报segmentation fault或者file not found而且出错时机很晚很难排查。所以一开始就按标准把骨架搭好后面省心。2.2 用busybox构建基础用户空间busybox是嵌入式Linux的瑞士军刀把几百个常用Linux命令塞进一个二进制文件里。我这里用busybox而不是buildroot或者yocto是因为对只跑几个固定应用的视频设备来说busybox完全够用而且裁剪方便、编译快。下载busybox源码后先配置交叉编译器tar xjf busybox-1.36.1.tar.bz2 cd busybox-1.36.1 make ARCHarm CROSS_COMPILEarm-himix100-linux- menuconfigmenuconfig里重点改三个地方Settings - Build static binary不选。动态编译体积小但要记得把动态库拷贝进rootfs见2.3节。静态编译虽然省事但可执行文件体积大而且后续第三方库都静态编不现实建议一开始就走动态。Settings - Cross compiler prefix填arm-himix100-linux-。Busybox Settings - Installation Options - Busybox installation prefix填你的rootfs绝对路径。配置完成后执行make ARCHarm CROSS_COMPILEarm-himix100-linux- -j8 make ARCHarm CROSS_COMPILEarm-himix100-linux- CONFIG_PREFIX/path/to/rootfs installinstall完成后rootfs/bin目录下会出现一堆指向busybox的符号链接ls、sh、mount、ifconfig这些命令就全都有了。这一步做完rootfs已经有了一个最小可用Linux用户空间。2.3 补齐glibc动态库动态编译方式下busybox以及后面的应用程序在运行时依赖glibc库。很多新手第一次烧完系统启动到一半卡住或者报cant load library libm.so.6就是因为忘记了拷库这一步。交叉工具链的sysroot里能找到全套目标板需要的库文件。路径可以用编译器的内置路径查看arm-himix100-linux-gcc -print-sysroot假设输出是/opt/hisi-linux/x86-arm/arm-himix100-linux/target那么动态库在target/lib目录下。拷贝原则是把整个lib目录都拷过来别自作聪明只挑几个libc文件因为你后面拷进来的第三方程序还会依赖libstdc、libpthread、librt这些这次不拷下次还得补。cp -a /opt/hisi-linux/x86-arm/arm-himix100-linux/target/lib/* rootfs/lib/注意用cp -a保留符号链接和权限。glibc的so文件本身有很多符号链接关系如果直接cp不带-a链接关系会断掉运行时会报版本不匹配。另外还要处理ld-linux路径。Arm 32位平台下动态链接器通常是/lib/ld-linux-armhf.so.3Hi3559AV100默认跑32位用户态即使芯片本身是A73 64位核很多项目仍按32位系统交叉编译确保这个文件在rootfs/lib下就行。2.4 创建设备节点Linux系统启动时内核会挂载devtmpfs并在/dev下自动创建设备节点但最开始的几个基础节点必须在rootfs里静态创建好否则内核还没挂devtmpfs之前就会报错。这几个节点是console、null、zero、ttyS0等。cd rootfs mkdir dev sudo mknod -m 666 dev/console c 5 1 sudo mknod -m 666 dev/null c 1 3 sudo mknod -m 666 dev/zero c 1 5 sudo mknod -m 666 dev/ttyS0 c 4 64主设备号次设备号别记错console是c 5 1null是c 1 3zero是c 1 5ttyS0是c 4 64。创建设备节点需要root权限所以用了sudo。如果你后面在文件系统起来后需要更多设备节点可以由mdev或者udev动态生成这里只保证能启动即可。2.5 配置文件与启动脚本一个能跑起来的根文件系统至少要有inittab、rcS、fstab、profile这几个基础文件。它们负责初始化系统环境、挂载虚拟文件系统、配置shell环境变量。/etc/inittab::sysinit:/etc/init.d/rcS console::askfirst:-/bin/sh ::ctrlaltdel:/sbin/reboot ::shutdown:/bin/umount -a -r这里第一个条目指定系统初始化执行rcS脚本第二个条目让串口控制台上可以直接交互进入shell这是调试阶段必须要有的。/etc/init.d/rcS是需要用户创建的脚本记得加可执行权限#!/bin/sh mount -t proc none /proc mount -t sysfs none /sys mount -t tmpfs none /tmp mount -t devtmpfs devtmpfs /dev mkdir -p /dev/pts mount -t devpts devpts /dev/pts echo /sbin/mdev /proc/sys/kernel/hotplug mdev -s这里挂了proc、sysfs、tmpfs、devpts并且启用mdev动态设备管理。mdev是busybox自带的可以根据内核uevent自动创建/删除设备节点比手工mknod省事太多。/etc/fstab# device mount-point type options dump fsck none /proc proc defaults 0 0 none /sys sysfs defaults 0 0 none /tmp tmpfs defaults 0 0fstab用来给mount -a命令使用的rcS里暂时没用到但保底留着没坏处。/etc/profileexport PATH/bin:/sbin:/usr/bin:/usr/sbin export HOSTNAMEHi3559 export PS1[\u\h:\w]# 这个文件让交互shell看起来舒服调试的时候一眼能看到当前目录。做主文件系统到这里目录骨架和基础配置就有了先用NFS把这块rootfs跑通验证完再打包镜像烧录。别急着打包先验证能省一大半排错时间。3. 制作ext4镜像文件3.1 计算镜像大小与参数预估rootfs目录搭好了下一步是把它打包成可以被内核mount的ext4镜像文件。第一步是确定镜像大小这个凭感觉很容易翻车。大小估算公式很直接先用du统计rootfs实际占用空间再在这个基础上加20%-30%余量同时留出日志空间和后续程序运行产生数据的空间。我这里有一个实际的例子。du -sh rootfs/ # 示例输出128M128MB的实际内容我一般直接生成256MB的镜像。200MB显得太紧运行几天日志一涨空间就见底系统会出各种莫名其妙的问题。当然如果你的方案是日志和数据单独分区挂载rootfs镜像可以只留10%的空闲避免空间浪费。镜像大小对应到磁盘上就是扇区数256MB等于262144个扇区。这个数等会儿分区和烧录时还要用到。inode数量也要算一下。mkfs.ext4默认每16KB数据一个inode128MB内容大约对应20000个文件256MB分区按默认设置生成的inode数通常够用。如果你要部署的应用程序里包含大量小文件可以在格式化时用-i 4096增加inode密度否则文件多了文件系统会报no space left on device但df -h却显示还有空间这种情况很容易被误判。3.2 用mkfs.ext4直接生成镜像最直观的做法是先创建一个固定大小的空文件再用mkfs.ext4格式化最后把rootfs内容拷贝进去。dd if/dev/zero ofrootfs.ext4 bs1M count256 mkfs.ext4 -b 4096 -i 8192 -m 1 -F rootfs.ext4参数解释-b 4096块大小4KB。对于eMMC这类Flash介质4KB块大小和Flash页大小对齐读写性能更好。ext4最大支持4KB块所以这是最稳妥的选择。-i 8192每8KB一个inode。文件数量较多的时候可以提高inode密度。-m 1预留1%空间给root用户。默认是5%对256MB分区来说就是浪费12MB而且嵌入式系统一般只有root用户1%够了。-F强制格式化因为目标是普通文件而不是块设备不加-F会提示确认。格式化完成之后使用loop设备把这个镜像挂载到宿主机目录内容整体拷贝进去。sudo mkdir -p /mnt/rootfs sudo mount -o loop rootfs.ext4 /mnt/rootfs sudo cp -a rootfs/* /mnt/rootfs/ sudo umount /mnt/rootfs这里有一个容易忽略的细节cp -a会把rootfs目录下的符号链接、文件权限、时间戳原样保留。如果用了普通cp -r符号链接可能被解引用成实际文件或者权限丢失系统起来后会出现各种奇怪的问题。另外拷贝时排除掉rootfs下已存在的挂载点目录内容如proc、sys、dev里的临时内容否则挂载后proc和sys里的东西会占空间。还有一点镜像mount之后boot目录、dev目录如果不在根文件系统里拷贝之前先在挂载后的目录里创建好确保和rootfs原始目录结构一致。3.3 镜像完整性检查制作镜像的最后一步是检查。这一步千万别省上次我没做检查直接烧板结果启动后文件系统只读挂载排查半天才发现是镜像制作时inode table损坏。检查命令是e2fscke2fsck -f -y rootfs.ext4-f强制检查即使文件系统看起来正常-y自动修复所有发现的问题。e2fsck会把不干净的卸载标记、坏块表、孤儿inode全部处理掉。这一步通过之后可以把镜像重新挂载到/mnt/rootfs检查一下目录里关键文件是否存在sudo mount -o loop rootfs.ext4 /mnt/rootfs ls -l /mnt/rootfs/bin/busybox ls -l /mnt/rootfs/etc/inittab sudo umount /mnt/rootfs如果busybox、inittab、rcS这些核心文件都在文件类型和权限没问题这个镜像就可以进烧录流程了。4. 烧录eMMC的几种方式与实操4.1 HiTool烧录方式详解HiTool是海思官方提供的图形化烧录工具在Windows下操作。单板调试阶段用它最顺手特别是第一次给空板烧fastboot的时候除了这种方式基本没有别的入口。操作流程板子设置成烧录模式通常是uboot启动阶段在串口敲回车进入命令行后执行对应命令或者拨码开关切到烧录模式连接网线和串口在HiTool里选择芯片型号为Hi3559AV100传输方式选网口按照分区表依次添加fastboot、内核uImage、dtb、rootfs.ext4镜像。有一点要注意HiTool烧录时是逐个分区写入的分区名和偏移地址必须和uboot环境变量里的mtdparts或者fastboot分区表完全一致。比如fastboot在mmcblk0p1内核在mmcblk0p2dtb在mmcblk0p3rootfs在mmcblk0p4那么HiTool里也要按这个顺序配置否则烧完引导不起来。HiTool烧录的缺点是它依赖Windows环境而且图形化界面在批量生产时不够灵活。我一般只在第一次给板子烧fastboot或者救砖时用它后面还是切到命令行方式。4.2 通过uboot的mmc命令烧录uboot里的mmc写命令是一次非常强大的救砖和量产工具不需要额外硬件只需要串口网线。原理是把镜像先下到内存再通过mmc write写入eMMC的指定扇区。先把开发板和电脑用网线直连电脑开tftp服务把rootfs.ext4放在tftp目录下。板子上电在uboot命令行里操作setenv ipaddr 192.168.1.20 setenv serverip 192.168.1.10 ping 192.168.1.10 tftp 0x42000000 rootfs.ext4tftp下载完成后确认下载大小print filesize假设rootfs.ext4大小是256MB即0x10000000字节。换算成512字节扇区数就是0x80000。如果你的rootfs是其他大小记得重新算一下。先查看当前eMMC分区表mmc list mmc dev 0 mmc part假设rootfs对应的是mmcblk0p4起始扇区是0x80000第4分区偏移那么写入命令是mmc write 0x42000000 0x80000 0x80000这里第一个0x80000是内存里镜像数据的起始地址对应的写入目标分区起始扇区第二个0x80000是要写入的扇区数。写完之后mmc dev 0确认环境变量已经指向eMMC启动然后reset重启。这种方法的关键点在于分区偏移地址必须准确。实际项目中建议先用mmc part或mmc read把分区表信息打印出来核对避免手滑写错位置把bootloader覆盖了。4.3 从SD卡启动后写入eMMC量产批量烧录的时候我更喜欢SD卡启动然后dd写入eMMC这种方式。前提是板子支持SD卡启动且已经有烧好系统的SD卡。原理很简单先让板子从SD卡把Linux跑起来然后在Linux环境下直接操作eMMC块设备把镜像dd进去。具体做法# 在SD卡启动的Linux环境里 ls /dev/mmcblk0* # eMMC设备可能是mmcblk0 ls /dev/mmcblk1* # SD卡设备可能是mmcblk1先把rootfs.ext4拷贝到SD卡或通过tftp下载到内存tftp -g -r rootfs.ext4 192.168.1.10然后写入eMMC对应分区。假设eMMC的rootfs分区是/dev/mmcblk0p4dd ifrootfs.ext4 of/dev/mmcblk0p4 bs4M sync这种方式的优点是可以在写入前对eMMC进行完整分区表初始化也可以用mmc erase命令先擦除再写入。重点在于确认eMMC在Linux下的设备名到底是mmcblk0还是mmcblk1——搞反了把SD卡覆盖了那就得重新做启动卡非常尴尬。确认方式很简单读取设备大小对比容量eMMC容量通常是板子标称的存储大小SD卡是另外的容量。4.4 设置uboot环境变量与内核启动参数烧完镜像之后如果直接从eMMC启动uboot必须知道两件事bootcmd如何加载内核和dtb和bootargs传给内核的启动参数。这是我调试中反复遇到的问题点配置文件对不上直接导致启动卡死或者根文件系统挂载失败。uboot环境变量设置参考setenv bootargs mem1G consolettyAMA0,115200 root/dev/mmcblk0p4 rootfstypeext4 rw init/linuxrc setenv bootcmd mmc read 0 0x42000000 0x10000 0x8000; mmc read 0 0x44000000 0x18000 0x2000; bootm 0x42000000 - 0x44000000 saveenv逐个解释mem1G指定内核可见内存大小根据硬件实际内存修改。consolettyAMA0,115200串口控制台。Hi3559AV100平台要注意实际用哪个串口有些板子是ttyAMA0有些是ttyS0对照板卡原理图确认否则启动过程完全看不到输出。root/dev/mmcblk0p4根文件系统所在分区。这里p4和你实际烧录的分区号必须一致。rootfstypeext4告诉内核根文件系统类型避免内核自动探测出错。rw可读写挂载这是跑业务的基本前提。init/linuxrc指定init进程路径。busybox安装时默认生成linuxrc指向/bin/busybox。bootcmd里的mmc read参数依次是读到内存地址、源起始扇区、读取扇区数。这里假设内核在分区2起始扇区0x10000dtb在分区3起始扇区0x18000。实际数值要按分区表来。4.5 内核配置与设备树中的eMMC节点烧录和启动参数都对但内核识别不到eMMC这种情况也不少见。物理连接没问题的话问题基本出在内核配置和设备树上。内核需要开启MMC子系统驱动和eMMC相关配置CONFIG_MMCy CONFIG_MMC_SDHCIy CONFIG_MMC_SDHCI_PLTFMy CONFIG_MMC_DWy (新版本SDHCI平台使用) CONFIG_MMC_BLOCKy CONFIG_EXT4_FSy如果CONFIG_MMC_BLOCK没开内核里根本不会出现mmcblk0设备节点uboot能读写eMMC不代表内核也能读。Hi3559AV100的设备树文件里sdk通常已经预设了sdhci或dwmmc节点但需要确认status设置为okaymmc-hs400-1_8v之类的时序属性是否正确。eMMC是否以HS400模式工作可以看内核启动log里打印的mmc0: new HS400 MMC card信息。HS400模式对PCB布线要求高如果板子硬件不过关反而会因为信号质量问题导致读写报错这时候可以退到HS200甚至HS50模式在设备树里把timing相关属性降级稳定优先。5. 常见问题与排查心得5.1 启动到一半卡死或反复重启这类问题排在嵌入式Linux调试遇坑榜的第一名。现象是uboot正常引导内核但serial console输出几行后就停在某个地方不动或者隔几秒自动重启。两个排查方向第一确认bootargs里的console参数和实际debug串口匹配第二确认内核启动过程中是否成功挂载了rootfs。如果VFS: Mounted root (ext4 filesystem) on device 179:4.这一行打印出来了说明内核已经成功挂载根文件系统之后卡死一般就是用户空间初始化问题。最常用的定位手段是给内核传init/bin/sh或init/bin/bash参数绕过inittab和rcS直接进shell。如果进了shell之后手动执行rcS能跑通那就是rcS里的某个命令在pid 1环境下有问题比如依赖网络但网络还没准备好如果进不了shell那就是busybox的符号链接、glibc库、设备节点有问题。还有一次我遇到的情况比较诡异uboot、内核、rootfs全部正常一启动就反复重启查到最后是rootfs/etc/inittab里没有::shutdown:/bin/umount -a -r这种条目导致的。严格说这不是重启原因而是某个服务崩溃触发内核panic之后watchdog自动复位而panic信息又没打出来。后来在内核bootargs里加了panic1等内核panic后直接重启简化了排查过程。5.2 ext4文件系统变成只读挂载明明bootargs里写了rw进系统之后mount一看还是ro或者运行过程中突然变成只读。这个现象出现的原因通常是ext4检测到文件系统错误或未正常卸载自动remount为read-only保护数据。排查命令首选dmesg看有没有EXT4-fs error (device mmcblk0p4): ext4_check_descriptors之类的日志。解决办法是在宿主机上对ext4镜像文件做一次完整的e2fsck流程我在3.3节提过e2fsck -f -y rootfs.ext4如果文件系统里有大量orphaned inode或者损坏的目录项e2fsck会尝试修复或放入lostfound。修复完成后重新mount验证。另外确认一下系统里有没有人误改过fstab或者是否开启了systemd的remount-ro逻辑嵌入式busybox系统一般不涉及这些。这里顺便补充一点关于fstrim和eMMC的关系。fstrim命令向文件系统未使用的块发送discard请求这在SSD上能有效减少写放大。对于eMMC来说协议层面支持discard但实际效果取决于eMMC内部固件的实现。我实测过部分eMMC芯片执行fstrim后掉电重挂载时反而会出现明显的卡顿或者性能下降推测是固件的垃圾回收策略保守导致的。所以在Hi3559AV100平台上我不会默认在rcS里加fstrim除非确认这颗eMMC对discard命令的支持很完善。如果确实要用建议先用一轮完整的读写压测和掉电测试确认稳定再开。5.3 eMMC分区表与容量问题Hi3559AV100的eMMC分区表通常写在uboot的mmc part命令输出里也可以烧完系统后进Linux用cat /proc/partitions查看。经常遇到的问题有三种。第一种是rootfs分区容量太小写日志写满了业务进程跑着跑着报错。这种在设计分区表的时候就要留足余量。我的习惯是日志和临时数据单独划一个分区挂到/var/log或/opt/datarootfs只放系统和应用程序这样就算日志写满也不影响系统分区。第二种是uboot下载镜像到内存后写入时发现source address不对齐。mmc write要求内存地址按4字节对齐实际上建议512字节对齐我们通常用0x42000000这种已经对齐的地址问题不大。第三种是eMMC容量和分区表对不上一般出现在换物料换容量或者二手板子上。比如原分区表按8GB规划实际板子是4GB eMMC烧录的时候就会因为写入越界失败。遇到这种就重新规划分区表别硬来。另外提一下热词里有人问“linux查看emmc内部垃圾”这类问题。eMMC内部确实有类似SSD的垃圾回收过程但它是内封闭的Linux用户态一般没法直接干预。mmc-utils工具里的mmc status get可以读取eMMC的life time estimation等健康信息但这和传统意义上“清理垃圾文件”完全是两码事。在文件系统层面能看到的就是目录里的文件占用想释放空间就正常删文件然后考虑要不要对分区做trim。5.4 Windows下查看ext4镜像内容调试过程中经常有硬件同事或者生产同事需要在Windows环境确认一下镜像里的文件对不对。Windows原生不支持ext4但有几个可行的思路。最轻量的是用DiskInternals Linux Reader这类工具只读查看ext2/3/4文件系统的内容拷出文件没问题。还有7-Zip新版也能直接打开ext4镜像文件当压缩包浏览。如果镜像只有64MB、128MB这种小体积在Win10/Win11上用WSL2把镜像挂载进去也是一个办法sudo mkdir /mnt/ext4 sudo mount -o loop /mnt/c/work/rootfs.ext4 /mnt/ext4比任何第三方图形工具都好用就是初始配置WSL2稍微折腾一点。不管用什么方法Windows下读ext4镜像我都不建议做写操作只读确认文件就够了。真要改文件还是回到Linux宿主环境去改避免因为工具兼容性问题把镜像搞坏。5.5 eMMC坏道与写放大问题热词里有一条“通过文件系统来屏蔽坏道的方法”如果是从机械硬盘时代过来的开发会有这类习惯性思维。但eMMC的情况完全不同它内部有FTL和坏块管理逻辑块到物理块之间的映射、坏块替换、磨损均衡全部由控制器主导操作系统和文件系统根本接触不到物理扇区。所以“在文件系统层屏蔽eMMC坏道”这个思路本身就不适用。不过eMMC的写放大问题值得重视。eMMC的最小擦除单位比页大频繁随机小块写入会导致内部垃圾回收压力增大即所谓的写放大。优化方向有两个一是在文件系统挂载时合理设置noatime避免访问文件时反复更新atime产生写请求二是把日志类数据集中到独立分区减少对rootfs分区的碎片化写入。Hi3559AV100如果跑视频流这类持续写负载建议外扩SSD或U盘做存储不要长时间高频率写eMMC这是很多项目前期评估容易忽略的点eMMC虽然比SD卡耐用但寿命也不是无限的。如果必须长时间写eMMC日志可以考虑定期断电重启加文件日志轮转别让日志把分区写满。6. 量产与交付经验6.1 镜像版本管理与校验等单板调试稳定之后就该考虑咋把镜像安全地部署到产线。很多朋友觉得参考设计没问题就甩给工厂做了结果工厂反馈“烧录后有的板子启动报错”浪费大量时间差旅。这里有三个习惯值得养成。第一个是版本号管理。每个镜像文件的文件名里带上日期和版本号比如rootfs_v1.2.3_20250314.ext4同时生成md5sum校验文件。uboot和内核镜像也统一命名规则。有了版本号工厂那边反馈问题时能快速定位烧的是哪个版本避免鸡同鸭讲。第二个是烧录完成后验证。HiTool批量烧录很慢如果板子数量大可以考虑脚本化自动烧录。产线可以用SD卡启动系统后自动从服务器拉取镜像写入eMMC关键是写完以后程序会读取文件系统里的关键文件做md5比对比对通过才pass。Hi3519系列有很多量产工具Hi3559AV100也类似能不用手工操作就不用人容易出错机器不容易出错。第三个是镜像备份。原厂SDK镜像、自己build的rootfs、最终量产镜像分三个目录存放尽量都放一台固定的文件服务器上。我遇到过一次工作电脑硬盘坏了rootfs源码都在但toolchain和SDK版本装不回来白白花了三天重新配环境。现在所有版本文件都同步到服务器和本地网盘双备份。6.2 产线自动化烧录脚本示例如果你有几十上百块板子要烧手动一条一条在uboot里敲命令显然不现实。用SD卡自动烧录方案比较稳。做法是做一个SD启动卡系统起来后自动执行一个脚本自动完成检测eMMC、擦除、分区、烧录、校验的全过程。在目标的根文件系统的/etc/init.d/rcS末尾加入如下逻辑if [ -f /auto_flash_flag ]; then /usr/bin/flash_emmc.sh fiflash_emmc.sh核心内容大致是#!/bin/sh # 分区表初始化 if [ -b /dev/mmcblk0 ]; then DEV/dev/mmcblk0 elif [ -b /dev/mmcblk1 ]; then DEV/dev/mmcblk1 else echo no emmc device exit 1 fi dd if/dev/zero of${DEV} bs1M count16 sync # 使用parted或fdisk创建分区表 # 示例创建4个分区 parted -s ${DEV} -- mklabel gpt parted -s ${DEV} -- mkpart fastboot 1MiB 10MiB parted -s ${DEV} -- mkpart kernel 10MiB 30MiB parted -s ${DEV} -- mkpart dtb 30MiB 40MiB parted -s ${DEV} -- mkpart rootfs 40MiB 100% # 写入镜像 dd if/rootfs.ext4 of${DEV}p4 bs4M sync echo flash done poweroff脚本开头的设备检测很关键SD卡和eMMC在各个平台上的设备号并不是统一不变的写死了风险很大。实际量产时我还会在脚本里加入镜像md5校验逻辑烧完之后读出来再算一次md5不一致就报警红灯一致就绿灯放行这样产线上的问题能第一时间暴露。6.3 研发期和量产期工具链差异注意研发阶段用NFS和tftp调试方便系统在内存里跑每次都相当于全新启动。量产阶段就必须面对真正的rootfs持久化运行。这时候要特别注意flash介质和NFS在I/O行为上的差异NFS上稍微慢一点没感知的操作在eMMC上可能就会因为落盘问题导致文件系统损坏。比如有的项目在rcS里直接对/var下的文件做频繁读写NFS调试时完全正常到了eMMC上用了几天就报文件系统错误。这种问题根源是rootfs虽然声明了rw但大量随机小写导致eMMC内部垃圾回收压力大最后触发控制器异常。所以量产前务必做长时间稳定性压测我一般至少让样机跑72小时全功能测试重点观察dmesg里有没有mmc相关报错再看分区剩余容量变化趋势。Hi3559AV100这颗芯片的SDK里自带了SD卡烧录工具但使用它们之前先确认一下版本。SDK版本不同烧录脚本、分区布局、甚至内核配置都可能不一样网上很多教程基于的SDK版本比较老照搬容易出现配置项名称对不上的情况。6.4 把镜像交给产线的规范文档最后分享一个比较实用的经验。给产线或协作同事交付镜像的时候我会同时提供一份简短的README内容包含三块版本信息每个镜像的md5值、编译日期、烧录方式HiTool配置截图或uboot命令列表、检验方法启动后执行什么命令确认版本号。这份README不需要写代码就是给操作的人看的。有一次产线反馈系统起不来远程排查了很久最后发现是同事拿错了内核镜像烧了上一个项目的uImage。从那以后我坚持每个项目一个独立交付目录目录名带项目代号镜像文件名带日期README里写明这个镜像对应的硬件版本、内存容量、eMMC容量。这些信息看起来琐碎但在多项目并行时能救命。7. 调试技巧与我个人的使用体会实际调试H3559AV100这类平台我建议大家第一步做的不是直接烧eMMC而是先用tftp nfs把系统跑起来验证内核配置和rootfs基础功能。NFS起来后所有rootfs的修改都不需要重新烧写直接在服务器端改板子上重启或重新挂载就生效。这个阶段的时间投入会在后续调业务功能时几十倍地赚回来。等NFS跑通业务程序、库文件、启动脚本全部验证完毕再把它固化成烧录镜像。固化之前一定要做一件事关闭调试期间留下的网络文件系统挂载配置把rcS里的NFS相关mount注释掉把profile里的调试环境变量清除。不然镜像烧到eMMC上板子在没有NFS服务器的环境里会卡在mount NFS超时上启动慢得让人以为系统挂了。另外一个常见的坑是rootfs里忘了放时区文件或者systemd的resolv.conf链接。Hi3559AV100跑视频应用时间戳错了会影响流媒体录制和事件上报。建议在rootfs的etc目录里预置/etc/TZ和/etc/resolv.conf或者在rcS里用busybox的date命令做一次时间初始化保证系统上电后有一个合理的时间参考。还有一个小细节/etc/init.d/rcS脚本里最后别忘了执行/bin/hostname设置主机名很多多媒体框架依赖hostname生成默认日志文件名没设主机名会出现奇怪的拼接错误。调试串口权限也值得提一下。在rootfs里给/bin/busybox加上suid权限或直接以root身份运行能省去很多tty设备权限问题chmod 4755 rootfs/bin/busybox做完这些再做一次完整断电报文确认日志正常eMMC根文件系统反复断电重启都不会损坏ext4 eMMC这套方案才算真正达到量产水准。这招是我在无数块开发板上熬出来的经验希望对同样在搞Hi3559AV100或者其他海思平台的朋友有帮助。
返回列表