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 系统分区布局:

典型车载 OTA 分区布局(A/B 系统) Bootloader (U-Boot / ABL) Boot A (ext4) Boot B (ext4) System A (SquashFS + overlay) System B (SquashFS + overlay) User Data (ext4 / UBIFS) OTA Metadata (ext4) — 断点续传状态、升级进度、校验信息 Misc / Recovery (ext4) — 恢复模式、日志、回滚标记 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 升级的骨架才算搭稳了。