3. Linux 文件系统基础:关键文件系统在 OTA 中的应用与分区布局
做车载 OTA 升级,文件系统是绕不开的坎。我刚开始接触这个领域时,以为文件系统就是存数据的地方,后来才发现——选错文件系统,升级方案直接废掉一半。
今天咱们就聊聊三种在 OTA 里最常见的文件系统:ext4、UBIFS、SquashFS。它们各自擅长什么?在分区里怎么摆?我踩过的坑,一并说清楚。
3.1 三种文件系统的核心差异
先看一张对比表,心里有个底:
| 特性 | ext4 | UBIFS | SquashFS |
|---|---|---|---|
| 存储介质 | 块设备(eMMC、SD) | 原始 NAND Flash | 块设备(只读) |
| 读写属性 | 可读写 | 可读写 | 只读 |
| 压缩支持 | 透明压缩(需配置) | 内置 LZO/zlib | 内置 gzip/lzo/xz |
| 掉电安全 | 日志(jbd2) | 日志(UBI 层) | 天生只读,无需考虑 |
| 磨损均衡 | 无(依赖硬件) | UBI 层提供 | 无 |
| 典型 OTA 用途 | 系统分区(A/B 槽) | 数据分区、小容量系统 | 只读根文件系统、固件镜像 |
说白了,ext4 是通用型,UBIFS 专治 NAND Flash 的坏块问题,SquashFS 则是「写一次,读一辈子」的压缩只读方案。
3.2 ext4:OTA 升级的主力军
ext4 在车载 Linux 里用得最多。为什么?因为 eMMC 已经成为主流存储,而 ext4 对 eMMC 的支持非常成熟。
我参与的第一个 OTA 项目,用的就是 ext4 做 A/B 分区。当时遇到一个问题:升级过程中突然掉电,重启后系统检查到日志,自动回滚到上一个版本。嗯,这就是 ext4 的 jbd2 日志模块在起作用。
关键点:ext4 的日志模式有三种——data=ordered(默认)、data=writeback、data=journal。OTA 场景下,我建议用 data=ordered,它保证元数据先于文件数据落盘,掉电后不会出现「文件存在但内容为空」的尴尬。
挂载参数示例:
# 典型的 OTA 系统分区挂载
mount -t ext4 -o noatime,nodiratime,data=ordered,barrier=1 /dev/mmcblk0p2 /system
这里 barrier=1 是重点。它强制写屏障,确保数据顺序写入。代价是性能略降,但换来了可靠性——OTA 升级时,这个取舍值得。
3.3 UBIFS:NAND Flash 的守护者
有些老平台还在用原始 NAND Flash,这时候 ext4 就不太合适了。NAND 有坏块、有位翻转,ext4 处理不了这些。
UBIFS 是 UBI 层之上的文件系统。UBI 负责磨损均衡和坏块管理,UBIFS 负责文件操作。两者配合,能延长 NAND 寿命。
我记得有一次,客户反馈升级后系统频繁崩溃。排查下来,是 UBIFS 的 LEB(逻辑擦除块)空间不足,导致写入失败。后来调整了 UBI 卷大小,问题解决。
经验之谈:UBIFS 的 ubi.mtd= 参数要预留 20% 的空闲空间给 UBI 层做磨损均衡。别把卷填满,否则写入性能会断崖式下跌。
挂载示例:
# UBIFS 挂载(假设已 attach UBI 设备)
ubiattach -m 3 -d 0
mount -t ubifs ubi0:rootfs /mnt/rootfs
3.4 SquashFS:只读系统的利器
SquashFS 是我个人最喜欢的文件系统。它只读、高压缩、加载快。在 OTA 场景里,它通常用来存放根文件系统或固件镜像。
你想想看,系统运行时不需要修改根文件系统,那为什么不把它压缩起来?SquashFS 能把一个 500MB 的根文件系统压到 150MB 左右,节省大量存储空间。
我曾经在一个项目里,把系统分区从 ext4 改成 SquashFS,配合 overlayfs 做数据分层。升级时只替换 SquashFS 镜像,用户数据保留在 overlay 层,断点续传的实现反而更简单了。
注意:SquashFS 是只读的,不能直接写入。如果需要修改文件,必须配合 overlayfs 或 tmpfs 使用。升级时,整个 SquashFS 镜像需要整体替换——这也是断点续传要处理的核心问题之一。
制作 SquashFS 镜像:
# 压缩根文件系统
mksquashfs /path/to/rootfs rootfs.squashfs -comp xz -b 128K
3.5 分区布局:OTA 升级的骨架
文件系统选好了,怎么摆到分区里?这是 OTA 升级方案的地基。我见过太多项目,分区布局没设计好,后期改起来痛不欲生。
下面是一个典型的 A/B 系统分区布局:
这个布局有几个设计要点:
- A/B 槽位隔离:Boot 和 System 各两份,升级时写入非活跃槽。如果升级失败,回滚到活跃槽即可。
- System 用 SquashFS + overlay:只读基座 + 可写 overlay,升级时只替换 SquashFS 镜像,overlay 层保留用户配置。
- OTA Metadata 分区:存放断点续传的进度、已下载块的校验和、升级状态。这个分区很小,但至关重要。
- User Data 独立:用户数据不参与 A/B 切换,升级前后保持一致。
避坑指南:我曾经在一个项目里,把 OTA Metadata 放在 User Data 分区里。结果用户恢复出厂设置时,Metadata 被清空,系统以为升级没开始,重新下载了整个包。后来我单独划了一个 16MB 的 ext4 分区给 Metadata,再也没出过问题。
3.6 分区大小怎么定?
这个问题没有标准答案,但有几个经验值可以参考:
| 分区 | 建议大小 | 说明 |
|---|---|---|
| Boot A/B | 64MB 各 | 内核 + dtb + initramfs,绰绰有余 |
| System A/B | 1.5x ~ 2x 原始大小 | SquashFS 压缩后 + overlay 预留空间 |
| User Data | 剩余空间 | 根据产品需求,至少 2GB 起步 |
| OTA Metadata | 16MB ~ 64MB | 够存断点续传状态 + 校验信息即可 |
| Misc / Recovery | 32MB | 存放恢复内核、日志、回滚标记 |
嗯,这里要注意:System 分区的大小要留余量。OTA 升级时,新镜像可能比旧的大一点。我见过一个项目,System 分区只留了 10% 余量,结果一次功能更新后镜像膨胀了 15%,升级直接失败。
3.7 文件系统与断点续传的关系
断点续传的核心是「记录进度 + 校验完整性」。文件系统在这里扮演什么角色?
- ext4:适合存储大文件(如升级包),支持随机读写,方便记录断点位置。
- UBIFS:适合 NAND 场景,但写入速度较慢,断点续传时要注意写入粒度。
- SquashFS:只读,不能直接写入。升级时需要在另一个分区下载完整镜像,然后整体替换。
我个人习惯的做法是:升级包下载到 User Data 分区(ext4),校验通过后,再写入 System 分区(SquashFS 镜像替换)。这样 User Data 的 ext4 负责断点续传的「下载阶段」,System 的 SquashFS 负责「安装阶段」。
小技巧:在 ext4 分区上做断点续传时,可以用 fallocate 预分配文件空间,避免写入过程中磁盘空间不足。我踩过这个坑——下载到 90% 时磁盘满了,续传时发现文件碎片化严重,校验失败。
好了,文件系统的基础就聊到这里。记住一句话:没有最好的文件系统,只有最合适的组合。ext4、UBIFS、SquashFS 各司其职,配合好分区布局,OTA 升级的骨架才算搭稳了。