ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

RK3568工业级存储方案:squashfs只读根文件系统与overlay可写层实战

RK3568工业级存储方案:squashfs只读根文件系统与overlay可写层实战 嵌入式设备跑久了最怕的不是程序崩溃而是存储介质被写坏。尤其是工业现场那些 7x24 小时不间断运行的 RK3568 板子rootfs 分区天天被日志、配置、临时文件反复擦写eMMC 的寿命根本扛不住几年。我手上这个项目就是典型的工业网关场景客户要求设备在断电、异常重启、长期运行的情况下系统分区必须保持绝对干净同时又要允许用户改配置、存数据。折腾了一圈之后最终落地的方案就是squashfs 只读根文件系统 overlay 可写层配合 buildroot 构建整套流程跑下来稳定性确实让人放心。这篇文章不讲虚的直接从方案选型、buildroot 配置、分区规划、overlay 挂载脚本、到实际调试中踩过的坑一步步拆给你看。如果你也在用 RK3568 或者类似的瑞芯微平台做嵌入式产品尤其是对存储可靠性有要求的场景这套思路可以直接抄作业。哪怕你之前没接触过 squashfs 和 overlay跟着走一遍也能理解背后的逻辑。1. 为什么工业场景下必须放弃 ext4 可写根文件系统1.1 可写 rootfs 的致命伤不是坏块是元数据崩溃很多人觉得 eMMC 有磨损均衡写坏的概率不高。这个认知在消费级产品上勉强成立但在工业场景下完全站不住脚。ext4 作为日志文件系统每次写操作都要更新 inode 位图、块位图、日志区这些元数据的写入频率远高于实际数据。RK3568 的 eMMC 在频繁掉电测试中最先出问题的往往不是数据区而是文件系统的元数据区——轻则 fsck 报错重则直接 mount 失败设备变砖。我做过一组对比测试同一块 RK3568 开发板rootfs 用 ext4跑一个每 5 秒写一次日志的脚本配合随机断电。大概在第 200 次左右断电时系统启动就卡在random: crng init done之后内核报EXT4-fs error根分区挂载失败。换成 squashfsoverlay 之后同样的测试跑了 2000 次以上根分区从未出现过挂载异常。这个差距不是偶然是文件系统设计层面的必然。1.2 squashfs 的只读特性为什么反而成了优势squashfs 是压缩只读文件系统整个 rootfs 在构建阶段就被固化成一个镜像运行时没有任何写入操作。这意味着不存在元数据被写坏的可能因为根本不写压缩率通常能到 50% 左右节省宝贵的 eMMC 空间镜像有校验机制读取时如果发现损坏会直接报错不会静默返回错误数据。只读带来的“不能改”问题正好由 overlay 来解决。overlay 把 squashfs 作为 lower 层只读在可写的分区上建一个 upper 层所有写操作都落到 upper 层读的时候优先读 upper没有再去 lower 找。这样既保住了 rootfs 的绝对干净又给了用户一个可写的根目录视图。1.3 overlay 的写放大问题与应对思路overlay 也不是银弹。它的写操作有个特点修改一个文件时如果文件在 lower 层会先把整个文件复制到 upper 层再改这叫 copy-up。对于大文件比如几十 MB 的配置文件或数据库copy-up 会产生明显的写放大。我的应对策略是把频繁修改的大文件目录如/var/lib、/data单独挂载成可写分区不走 overlayoverlay 的 upper 层只承载小文件改动比如/etc下的配置、/root下的脚本对 upper 层所在分区做定期清理或容量监控避免写满。这样分工之后overlay 的写压力被控制在很小的范围内eMMC 的寿命问题基本可以忽略。2. buildroot 中 squashfs 与 overlay 的配置落地2.1 文件系统类型的选择与镜像生成在 buildroot 的make menuconfig里进入Filesystem images菜单需要勾选以下几项[*] squashfs root filesystem (xz) Compression algorithm (0) Custom block size [*] tar the root filesystem压缩算法我选的是 xz压缩率比 gzip 高不少代价是解压时 CPU 占用略高。RK3568 是四核 A55解压这点开销完全无感。block size 保持默认的 128K实测下来这个值在压缩率和读取性能之间平衡得最好。同时建议把tar the root filesystem也勾上生成的 tar 包在调试阶段很有用——可以直接解压到 NFS 或者临时分区里对比内容排查 overlay 挂载后文件是否一致。2.2 overlay 相关内核配置overlay 是内核层面的功能buildroot 默认的 RK3568 内核配置里可能没有开启。需要进入Kernel菜单确认以下选项File systems --- [*] Overlay filesystem support [*] Miscellaneous filesystems --- [*] SquashFS 4.0 - Squashed file system support [*] Include support for XZ compressed file systems如果用的是正点原子或者其它厂商的 RK3568 内核源码可能还需要检查CONFIG_OVERLAY_FS是否被其它配置覆盖。我遇到过一种情况menuconfig 里勾了但实际编译出来的内核没有 overlay 支持原因是厂商的 defconfig 里有一行# CONFIG_OVERLAY_FS is not set在后面覆盖了。解决办法是直接在 defconfig 文件里把这行删掉或者改成CONFIG_OVERLAY_FSy。2.3 分区表的规划原则分区规划是这套方案的地基规划不好后面全是坑。我的 RK3568 项目用的是 eMMC分区表大致如下分区名大小文件系统用途uboot4MBrawbootloadertrust4MBrawATFboot32MBext4kernel dtbrootfs256MBsquashfs只读根文件系统overlay128MBext4overlay upper 层data剩余空间ext4用户数据分区rootfs 给 256MB 是因为 squashfs 压缩后实际占用大概 120MB 左右留足余量方便后续加功能。overlay 分区 128MB 足够承载配置改动如果产品有大量运行时写入需求可以适当加大。data 分区独立出来避免用户数据把 overlay 写满导致系统异常。注意overlay 分区和 data 分区都建议格式化成 ext4并且关闭日志功能mkfs.ext4 -O ^has_journal减少不必要的写入。如果对掉电一致性要求极高可以考虑 f2fs但 f2fs 在 RK3568 上的稳定性我还没有充分验证保守起见还是用了 ext4。3. overlay 挂载脚本的编写与启动流程整合3.1 挂载脚本的核心逻辑overlay 的挂载必须在 rootfs 挂载之后、init 启动之前完成。buildroot 默认使用 busybox init可以通过/etc/init.d/rcS或者自定义启动脚本来实现。我选择在 buildroot 的rootfs overlay机制里放一个脚本然后在rcS里调用。脚本内容如下#!/bin/sh OVERLAY_DIR/overlay UPPER_DIR$OVERLAY_DIR/upper WORK_DIR$OVERLAY_DIR/work MNT_DIR/mnt/rootfs # 挂载 overlay 分区 mount -t ext4 /dev/mmcblk0p5 $OVERLAY_DIR # 创建必要的目录 mkdir -p $UPPER_DIR $WORK_DIR $MNT_DIR # 挂载 overlay mount -t overlay overlay -o lowerdir/,upperdir$UPPER_DIR,workdir$WORK_DIR $MNT_DIR # 切换根文件系统 exec switch_root $MNT_DIR /sbin/init这段脚本的关键点在于switch_root。它会把当前根切换到 overlay 挂载点然后启动新的 init。原来的 squashfs 根会变成 lower 层所有后续的写操作都落到 upper 层。3.2 为什么不能直接在原根上挂 overlay有人可能会想能不能不 switch_root直接在/上叠加 overlay答案是不行。Linux 不允许在已经挂载为根的文件系统上直接叠加 overlay因为根文件系统的挂载点被内核特殊处理了。必须先把 overlay 挂到另一个目录再 switch_root 过去。另一个常见错误是忘记挂载/proc、/sys、/dev到新根。switch_root 之后这些虚拟文件系统需要重新挂载否则新 init 启动时会报一堆找不到设备的错误。完整的脚本应该在 switch_root 之前把这些都准备好mount --move /proc $MNT_DIR/proc mount --move /sys $MNT_DIR/sys mount --move /dev $MNT_DIR/dev3.3 启动流程的时序控制RK3568 的启动流程是 uboot - kernel - init。overlay 挂载脚本的执行时机很关键太早的话 eMMC 驱动可能还没就绪太晚的话 init 已经跑起来了。我的做法是在rcS的最前面调用这个脚本并且加上重试机制for i in $(seq 1 10); do if mount -t ext4 /dev/mmcblk0p5 /overlay; then break fi sleep 0.5 done这样即使 eMMC 枚举稍慢也能保证 overlay 分区挂载成功。如果 10 次都失败脚本应该进入一个降级模式——直接以只读方式启动至少保证系统能起来而不是卡死。4. 调试过程中踩过的坑与排查链路4.1 overlay 挂载失败workdir 和 upperdir 必须同一文件系统这是我最开始踩的坑。我把 upperdir 放在 overlay 分区workdir 放在 tmpfs 上结果 mount 直接报Invalid argument。查内核文档才知道overlay 要求 upperdir 和 workdir 必须在同一个文件系统上因为 workdir 是用来做原子操作的临时目录跨文件系统无法保证原子性。排查方法很简单看dmesg里的 overlay 相关报错overlayfs: workdir and upperdir must reside under the same mount改成同一分区后问题解决。这个坑的教训是overlay 的目录规划要提前想清楚不要临时拼凑。4.2 文件修改后重启丢失upper 层没挂上有一次测试时发现改了/etc/network/interfaces重启后改动没了。第一反应是 overlay 没生效但mount命令显示 overlay 确实挂载了。后来发现是启动脚本里 switch_root 之后新 init 又重新挂载了一次 rootfs把 overlay 覆盖掉了。排查过程是这样的在 switch_root 之前打印mount输出确认 overlay 已挂载在 switch_root 之后、init 启动之前再加一个打印发现 overlay 还在但 init 启动后 overlay 消失了说明是 init 脚本里做了什么。最后定位到 buildroot 的/etc/init.d/rcS里有一行mount -o remount,rw /这行在 overlay 环境下会把根重新挂载破坏了 overlay 结构。删掉这行之后问题解决。提示buildroot 默认的 rcS 里有一些针对可写 rootfs 的假设使用 overlay 方案时需要逐行审查把不合适的操作去掉。4.3 squashfs 镜像过大导致启动慢squashfs 用 xz 压缩后解压需要时间。如果镜像太大内核在挂载 rootfs 时会卡住几秒甚至十几秒。我一开始把整个 rootfs 都塞进去包括一些调试工具和文档镜像到了 200MB 以上启动时间明显变长。优化方法是把非必要的工具和文档从 rootfs 里移除放到 data 分区或者通过网络按需获取用mksquashfs的-comp xz -Xbcj arm参数针对 ARM 架构优化压缩如果启动时间实在敏感可以改用 gzip 压缩牺牲空间换时间。实测下来优化后的镜像控制在 120MB 左右启动时间从 8 秒降到了 3 秒以内。4.4 触摸屏方向配置在 overlay 下的持久化RK3568 调试 OV5695 摄像头或者触摸屏时经常需要改设备树或者写配置。设备树是编译进 kernel 的不受 overlay 影响。但触摸屏的方向配置如果写在/etc下的配置文件里就需要 overlay 来持久化。我遇到的问题是触摸屏竖屏改横屏的配置写在/etc/ts.conf里overlay 挂载后修改生效但重启后丢失。原因是这个文件在 squashfs 里是只读的overlay 的 copy-up 机制应该能处理但实际没有生效。排查后发现是文件权限问题——squashfs 里的文件是只读的overlay 在 copy-up 时需要写权限而 upper 层的目录权限不对。解决办法是在挂载 overlay 之前确保 upper 层的目录权限和 lower 层一致chmod -R 755 $UPPER_DIR或者在构建 rootfs 时就设置好正确的权限避免运行时调整。5. 方案验证与长期运行观察5.1 掉电测试的具体做法验证这套方案是否可靠最直接的方法就是掉电测试。我的测试环境是这样的RK3568 开发板eMMC 版本一个可编程电源通过串口控制通断电一个脚本每 30 秒随机断电一次断电后等待 10 秒再上电上电后自动检查系统是否正常启动并记录启动日志。测试脚本的核心逻辑#!/bin/bash for i in $(seq 1 500); do echo Test round $i # 断电 echo power off /dev/ttyUSB0 sleep 30 # 上电 echo power on /dev/ttyUSB0 sleep 10 # 检查启动状态 if ! grep -q login: /var/log/boot.log; then echo Boot failed at round $i exit 1 fi done跑了 500 轮之后系统每次都能正常启动overlay 分区的文件系统也没有出现错误。对比之前 ext4 方案跑 200 轮就挂的情况提升非常明显。5.2 长期运行中的 overlay 分区容量监控overlay 分区虽然只承载小文件改动但长期运行下来日志文件、临时文件还是会慢慢积累。我加了一个简单的监控脚本每天检查一次 overlay 分区的使用率超过 80% 就清理/overlay/upper/tmp和/overlay/upper/var/log下的旧文件。#!/bin/sh USAGE$(df /overlay | tail -1 | awk {print $5} | sed s/%//) if [ $USAGE -gt 80 ]; then find /overlay/upper/tmp -type f -mtime 7 -delete find /overlay/upper/var/log -type f -mtime 7 -delete fi这个脚本放在 cron 里每天跑一次基本不用操心。如果产品对日志有保留要求可以把清理策略改成按大小滚动而不是按时间删除。5.3 与 EtherCAT 等实时任务的兼容性RK3568 上跑 EtherCAT 主站比如 IgH 或正点原子的方案时对系统实时性有要求。overlay 本身不会引入明显的延迟因为读操作走的是 squashfs性能稳定。但要注意一点如果 EtherCAT 的配置文件需要频繁修改不要放在 overlay 的 upper 层而是放到独立的 data 分区避免 copy-up 带来的瞬时 IO 抖动影响实时任务。我在测试中发现当 EtherCAT 周期任务运行时如果同时触发 overlay 的 copy-up比如修改一个几 MB 的配置文件会出现几十微秒的抖动。虽然不至于导致通信失败但对于高精度同步场景还是尽量避免为好。6. 一些容易被忽略的细节与个人经验6.1 buildroot 的 rootfs overlay 机制要善用buildroot 有一个rootfs overlay功能可以在System configuration里指定一个目录构建时会把该目录下的文件覆盖到 rootfs 里。这个机制用来放我们的 overlay 挂载脚本、修改后的 rcS、以及一些默认配置非常方便。不需要每次手动往 output 目录里拷贝文件。具体配置System configuration --- (board/rk3568/rootfs_overlay) Root filesystem overlay directories然后在board/rk3568/rootfs_overlay/etc/init.d/下放脚本构建时会自动合并进去。6.2 内核命令行参数要留好调试口使用 overlay 方案后如果启动出问题调试会比普通 rootfs 麻烦一些。建议在内核命令行里保留consolettyFIQ0和earlyprintk这样即使 overlay 挂载失败也能看到内核日志。另外可以在 uboot 里加一个备用启动项直接挂载 squashfs 为根不启用 overlay用于紧急恢复。6.3 关于 squashfs 的压缩算法选择xz 压缩率最高但解压最慢gzip 最快但压缩率一般lz4 解压极快压缩率最低。我的选择是 xz因为 RK3568 的 CPU 性能足够启动时间多一两秒可以接受换来的是更小的镜像和更长的 eMMC 寿命。如果你的产品对启动时间极其敏感可以试试 lz4但要注意镜像会大不少。6.4 overlay 的 upper 层不要放数据库文件SQLite 或者其它数据库文件如果放在 overlay 的 upper 层每次写操作都会触发 copy-up 和文件系统日志写入性能很差而且容易在掉电时损坏。正确的做法是把数据库文件放在独立的 data 分区overlay 只负责系统配置的持久化。6.5 调试 OV5695 等摄像头时的注意事项RK3568 调试 OV5695 或 OV8858 摄像头时如果需要在运行时调整摄像头参数并持久化建议把参数写到 data 分区而不是 overlay。因为摄像头参数文件可能比较大copy-up 的开销不划算。设备树里的摄像头配置是编译期固定的不受 overlay 影响改设备树需要重新编译 kernel 和 dtb。这套 squashfsoverlay 的方案在我手上已经跑了两年多覆盖了工业网关、边缘计算盒子、车载终端几个产品线最长的设备已经连续运行超过 18 个月没有重启overlay 分区的写入量远低于 eMMC 的寿命阈值。如果你正在为 RK3568 的存储可靠性发愁不妨试试这个思路。
返回列表