背景:RK3568 板卡,eMMC 启动,根文件系统 ext4 挂载在 eMMC p7 上。想直接在系统运行时dd新版本镜像升级,到底行不行?讨论结论是:能不能 dd,不取决于介质(eMMC/NAND)、文件系统类型或烧写工具,只取决于一件事——dd 开始那一刻,目标分区对运行中的系统是不是"死"的(未挂载,或只读挂载且无写回)。
一、为什么代码跑在 DDR 里,还是不能直接 dd 正在用的分区?
上电时 U-Boot 把内核、dtb 读进 DDR,内核再按需把 rootfs 的页读进内存——但rootfs 的本体始终在 eMMC 上,运行期间内核持续和它交互:
- 可执行文件是 mmap 到 eMMC 块的,内存不够时按页重读;
- 日志、数据库、临时文件在持续写回;
- 文件系统元数据(inode、bitmap、journal)在内核与磁盘间不断同步。
直接 dd 覆写块设备会绕过文件系统层,与上述写回并发,几秒内就会把刚写的镜像冲坏,还会造成元数据与数据错位。这不是 dd 命令的问题,是"边跑步边换腿"。
二、分区能不能在线 dd 的判定标准
分区类型 | 运行时的状态 | 能否在线 dd | 说明 |
|---|---|---|---|
uboot / kernel / dtb / boot | 裸镜像,启动后不再读 | ✅ 随时 dd | 对系统来说等同"死盘" |
system(只读挂载) | ro 挂载,无写回 | ✅ 可 dd,写完立刻重启 | 无写回竞争;但读竞争仍在,dd 期间进程可能读到新旧混合的数据而崩溃 |
userdata(读写挂载) | rw 挂载,持续读写 | ❌ 必须先 umount 或借助 recovery 环境 | dd 与页缓存回写、journal 提交并发,必然损坏(多为静默损坏,"没异常"只是没立刻暴露) |
一句话:
dd 开始那一刻,目标分区的块设备上除了 dd 之外还有没有别的写入者。有,就不安全;没有,就安全。
三、为什么 NAND 方案"看起来"能在线升级?
不是因为 NAND 工艺特殊,而是架构决定的:
- 整系统跑在 RAM:uboot 把内核 + initramfs 全量加载进 DDR,rootfs 不挂载在 NAND 上,NAND 就是一块闲置盘,随便擦写;
- A/B 双分区:当前跑 A 槽,升级写空闲的 B 槽,写完切启动标志 reboot——写 B 时系统对 B 没有任何 I/O。
四、手机为什么能"直接 dd"?
手机 eMMC/UFS 是块设备(不是 MTD),机制与板子完全相同。刷机时 dd 的对象分两类:
- boot / dtbo / vbmeta / recovery:裸镜像分区,系统运行时不挂载、不读取,dd 天然安全;
- system / vendor:只读挂载(ext4 ro 或 erofs),内核不产生脏页、不提交 journal,无写回竞争,dd 完整覆盖后盘上就是完整新镜像。代价是 dd 进行中进程可能读到新旧混合的数据而崩溃,所以"dd 完马上重启"。
至于 userdata,主流 Android 并不存在"在线 dd 挂载中的 userdata 不出问题"——凡是成功案例,拆开看都是:进 recovery dd(那里 userdata 没挂载)、先 umount 再 dd、或 dd 的是闲置分区/未用空间。remount,rw / 与 dd 安全性无关:mount 的 ro/rw 是 VFS 层标志,管的是路径读写;dd 直接操作块设备,完全绕过 VFS。remount,rw只是升级脚本自己要用文件接口写文件,它既不保护也不妨碍 dd。
五、eMMC 在线升级的正确姿势
裸镜像分区(uboot/kernel/dtb/boot):任何时候可直接 dd。
system 类只读分区:
dd if=system_new.img of=/dev/mmcblk0pX bs=4M conv=fsync reboot -f # 写完立刻重启,缩短暂的新旧共存窗口
userdata 类读写分区:
sync umount /data # 或:mount -o remount,ro /data;fuser -k 清理占用进程 dd if=userdata_new.img of=/dev/mmcblk0pY bs=4M conv=fsync reboot -f
dd 前用这几条确认目标分区已"死":
cat /proc/mounts | grep mmcblk0 # 是否还挂着?什么模式? lsof +D /data | head # 谁还握着写句柄? cat /proc/meminfo | grep Dirty # 脏页是否已回写干净?
A/B 双槽是更稳妥的工程方案:当前槽运行时把新镜像写空闲槽,写完后改 uboot 环境变量切换启动槽。掉电安全(升级中途断电,环境变量没切,仍从旧槽启动),且无新旧共存窗口。
六、几条配套纪律
- dd 完整写完后盘上才是成套的新镜像,半截镜像 = 起不来的残次品;升级包先校验 sha256 再开写;
- 加
conv=fsync(或 dd 后手动 sync),确保数据真正落盘而非停在缓存; - eMMC 裸写偏移布局(RK 平台,512B 扇区):idbloader 偏移 64、uboot 偏移 16384、trust 偏移 24576,
conv=notrunc必须加,否则 dd 会截断整块盘; - 写 boot0/boot1 分区前记得解锁:
echo 0 > /sys/block/mmcblk0boot0/force_ro; - 动 uboot/trust/分区表等关键区域,优先走 SD 卡启动或 Maskrom(upgrade_tool),不要在线裸写正在运行的根设备。
核心结论:各种"专用烧写工具"(recovery、upgrade_tool 等)的全部意义,就是创造一个目标分区不被挂载使用的环境——达到同样的物理状态后,dd 和它们是等价的。判断安全性的唯一标准始终是:dd 那一刻,目标分区上除了 dd 还有没有别的 I/O。