嵌入式Linux文件系统选型实战squashfs、UBIFS、ext4到底怎么选一句话总结根文件系统用squashfs只读压缩数据分区用UBIFSNAND Flash或ext4eMMC/SD卡日志分区用tmpfs——这个组合解决了90%嵌入式产品的存储需求。本文给出完整的选型决策树和配置方案。做嵌入式Linux产品迟早会被一个问题卡住文件系统用什么我第一次做量产产品的时候就踩过这个坑。选了ext4做根文件系统结果产品在客户现场异常断电几次后文件系统损坏起不来了。后来换成squashfsoverlay的方案再没出过这类问题。今天就把我这些年做嵌入式Linux文件系统选型的经验全写出来。第一步先搞清楚你的存储介质嵌入式设备常用的存储介质就三种介质特点适合的文件系统NAND Flash有坏块、需要ECC、按页读写UBIFS, YAFFS2eMMC自带FTL、像普通块设备ext4, squashfsSD/NOR简单、容量小ext4, FAT选文件系统的第一件事不是看功能是看存储介质。NAND Flash绝对不能直接上ext4——ext4没有坏块管理在NAND上用几天就坏了。第二步分区方案是核心我做过的最稳定的分区方案长这样分区布局4GB eMMC为例 ┌─────────────────┐ │ boot (16MB) │ ← FAT16/vfat, 放kernelDTB ├─────────────────┤ │ rootfs (256MB) │ ← squashfs, 只读根文件系统 ├─────────────────┤ │ overlay (128MB) │ ← ext4, overlayfs上层 ├─────────────────┤ │ data (剩余空间) │ ← ext4, 用户数据 ├─────────────────┤ │ log (64MB) │ ← tmpfs, 运行时日志 └─────────────────┘这个方案的精髓是根文件系统只读运行时可写层叠在overlay分区上。这样根系统永远不会因为异常断电损坏恢复出厂设置也简单——清空overlay分区就行。第三步各种文件系统详解squashfs — 根文件系统的首选squashfs是一个只读、压缩的文件系统。它是我做嵌入式Linux首选根文件系统的原因有三个1. 压缩率高一个200MB的rootfs用squashfs能压到60-80MB。对于存储资源紧张的嵌入式设备这个压缩率太香了。# 制作squashfs根文件系统mksquashfs /path/to/rootfs rootfs.squashfs\-compzstd\-b131072\-all-root2. 只读即安全因为文件系统本身是只读的异常断电不会损坏它。你再也不用担心客户拔电源把设备搞挂了。3. 加载快squashfs支持随机访问内核只需要解压被访问到的数据块不是整个解压。启动速度比解压整个initramfs快得多。UBIFS — NAND Flash专用如果你的产品用NAND FlashUBIFS是最好的选择。它比YAFFS2新支持更好的压缩和磨损均衡。# 制作UBIFS镜像mkfs.ubifs-r/path/to/rootfs\-m2048\# 页大小-e124KiB\# 擦除块大小-c1024\# 最大逻辑擦除块数-orootfs.ubifs# 打包成ubi镜像ubinize-orootfs.ubi\-m2048\-p128KiB\ubinize.cfg关键参数说明-m页大小从NAND芯片手册看常见2048或4096-e擦除块大小一般是页大小×64-c最大擦除块数估算你需要的最大容量ext4 — eMMC/SD卡的标准答案eMMC自带FTLFlash Translation Layer坏块管理和磨损均衡都由eMMC控制器处理了。所以你可以把它当普通硬盘用ext4是最成熟的选择。# ext4调优参数适合嵌入式mkfs.ext4-O^has_journal\# 去掉日志省空间但异常断电可能丢数据-b4096\# 块大小4K-Estride16,stripe_width16\# eMMC对齐优化/dev/mmcblk0p3要不要关掉journal如果分区是用来放临时数据或缓存关掉journal能省不少空间和写入。如果放重要数据留着。tmpfs — 日志和临时文件# /etc/fstab 配置tmpfs /var/log tmpfs defaults,noatime,size64M,mode075500tmpfs /tmp tmpfs defaults,noatime,size32M,mode177700日志放tmpfs的好处系统重启后日志自动清空不会积累不会写坏Flash。第四步overlayfs实现只读根可写层这是我现在用的标准方案在Buildroot和Yocto里都支持。# overlayfs挂载脚本init阶段执行mount-tsquashfs /dev/mmcblk0p2 /mnt/rootfsmount-text4 /dev/mmcblk0p3 /mnt/overlaymkdir-p/mnt/overlay/upper /mnt/overlay/workmount-toverlay overlay\-olowerdir/mnt/rootfs,upperdir/mnt/overlay/upper,workdir/mnt/overlay/work\/mnt/merged# 最后switch_root到/mnt/mergedexecswitch_root /mnt/merged /sbin/init恢复出厂设置只需要格式化overlay分区根文件系统自动恢复出厂状态。选型决策树做新项目的时候直接按这个流程走NAND FlasheMMCSD卡大容量小容量要只读无所谓频繁写很少写存储介质是什么容量需求是否要只读根是否频繁写UBIFSYAFFS2squashfsoverlayext4ext4ext4调优兼容性好常见问题Q: 为什么不用JFFS2JFFS2太老了。它把整个文件系统的索引存在内存里512MB的Flash需要128MB RAM来存索引。而且挂载时要扫描整个Flash大容量下慢得离谱。UBIFS彻底解决了这些问题。Q: squashfs的压缩级别怎么选我一般用zstd或lz4。zstd压缩比高lz4解压速度快。如果启动速度是关键指标比如车载设备选lz4。如果Flash空间紧张选zstd。Q: 我的根文件系统需要更新squashfs只读怎么办两种方案1双分区A/B升级 — 两个squashfs分区轮流更新升级失败可以回滚。2只保留核心文件在squashfs里需要更新的部分如Qt库、应用二进制放在ext4分区。Q: uboot如何加载squashfs内核实际上uboot不需要理解squashfs。kernel和DTB单独放在一个FAT分区boot分区uboot从这里加载。squashfs只放根文件系统。# uboot脚本示例setenv bootargsroot/dev/mmcblk0p2 rootfstypesquashfs rofatload mmc0:1${loadaddr}zImage fatload mmc0:1${fdtaddr}${board}.dtb bootz${loadaddr}-${fdtaddr}总结嵌入式Linux文件系统选型没有完美的方案。最快的决策方法就是按上面那张决策树走。但如果你让我给一个最保险的默认方案eMMC存储squashfs(根) ext4(overlay数据)NAND FlashUBIFS(统一分区需要可写) 或者 UBIFS(只读根) UBIFS(数据)一定要做overlayfs— 只读根防止文件系统损坏需要写的地方用overlay层这套方案我用在量产项目上没有出过因为文件系统导致的严重故障。记住文件系统的可靠性不是选出来的是设计出来的。分区方案和异常处理逻辑比文件系统本身更重要。