root@cnblogs: ~/personal-blog

root@blog:~$ ./start-blog.sh

ACCESS GRANTED

[OK] Knowledge database connected

root@blog:~$ █

为什么写入过程中掉电、异常复位或 Flash 磨损引起,LittleFS 的失败概率一般会比FAT明显更低?

核心原因不是 LittleFS“不会损坏”,而是它把一次修改设计成:掉电后要么看到旧版本,要么看到完整的新版本,尽量不出现半新半旧的状态。

1. Flash 掉电时为什么容易损坏

Flash 有两个重要特性:

  • 写入通常只能把 bit 从 1 改成 0
  • 要恢复成 1,必须先擦除整个 erase block

如果编程或擦除过程中掉电,一个块可能处于不确定状态:

  • 一部分字节已经写入,另一部分没有写入
  • 擦除只完成了一部分
  • 控制器已经返回部分操作,但数据尚未真正稳定
  • 单个扇区更新可能在物理层变成“读整块、擦除、重写整块”

异常复位与掉电的效果类似:CPU、DMA 或存储控制器可能在更新元数据的中途停止。

2. FAT 为什么更容易出现“不一致状态”

FAT 更新一个文件通常需要修改多个相互关联的位置。例如向文件追加一个簇:

  1. 分配新簇
  2. 在 FAT 表中把新簇标记为已使用
  3. 修改旧簇的链表指针,使其指向新簇
  4. 写入新簇的数据
  5. 修改目录项中的文件大小、时间等
  6. FAT32 可能还会更新 FSInfo

这些操作通常不是一个原子事务。假设在不同时间掉电:

  • 新簇已分配,但目录项未更新:产生丢失簇
  • 链表已指向新簇,但数据还没写完:文件包含无效数据
  • 文件大小已增加,但簇链还没接好:大小与簇链不一致
  • 一份 FAT 表更新了,另一份还没有:两份 FAT 不一致
  • 正在重写目录项时掉电:目录项可能部分损坏

标准 FAT 没有事务日志,也没有明确的“这批修改已经完整提交”标志。重新挂载时,文件系统往往难以判断:

  • 哪些修改属于同一次操作
  • 应该保留旧状态还是新状态
  • 两份 FAT 中哪一份才正确

FAT 的两份 FAT 表主要是冗余副本,并不等同于 LittleFS 的两代事务版本。它们通常没有完整的版本选择和事务 CRC 机制。

需要注意:FAT 出现不一致不一定立即挂载失败。很多情况下仍能挂载,但需要 fsck/chkdsk 修复;严重损坏关键元数据时才可能挂载失败。

3. LittleFS 如何应对掉电

写时复制

LittleFS 修改数据时,通常不会先破坏当前有效版本,而是:

  1. 把新数据写到其他位置
  2. 写入新的元数据
  3. 计算并写入提交 CRC
  4. 完整提交后,新元数据才开始引用新数据
  5. 之后才回收旧数据

因此:

  • 在提交前掉电:新内容被忽略,旧内容仍然有效
  • 在提交后掉电:新内容有效
  • 提交写了一半:CRC 不通过,挂载时回退到上一次有效提交

这相当于把结果限制为“旧版本”或“新版本”,而不是 FAT 中可能出现的“链表是新的、文件大小是旧的、数据只写了一半”。

元数据对

LittleFS 的重要元数据通常保存在一个 metadata pair 中,即两个块配合使用。

更新过程大致是:

  • 当前块中保留有效提交记录
  • 新修改以日志形式追加
  • 每次提交带有 CRC
  • 空间不足时,将有效状态整理到另一个块
  • 新块完整有效之前,不销毁旧块
  • 通过 revision count 判断哪个块更新

如果掉电发生在整理或切换过程中:

  • 新块完整且 CRC 正确:使用新块
  • 新块不完整:继续使用旧块

只有两边都无法恢复时,这组元数据才真正不可用。

CRC 提交记录

LittleFS 的 CRC 不只是检查某个字段,它还用于识别一次提交是否完整。

例如写入到一半时:

旧提交:完整,CRC 正确
新提交:只写了一部分,CRC 错误

挂载时会扫描到最后一个完整且 CRC 正确的提交,将不完整的新提交丢弃。

CRC 的作用是检测损坏,不是修复任意损坏;真正保证可恢复的是“旧版本仍保留 + 新版本验证通过才生效”。

4. 异常复位为什么也是 LittleFS 更可靠

异常复位包括:

  • 看门狗复位
  • 软件崩溃后重启
  • CPU lockup
  • 总线错误
  • 用户强制重启

只要复位发生在文件系统写入期间,对存储介质来说与掉电类似。LittleFS 的提交边界可以让挂载过程识别最后一次完整事务。

不过应用仍应正确调用:

  • lfs_file_sync()
  • lfs_file_close()
  • lfs_unmount()

LittleFS 保证的是文件系统结构尽量保持一致,不保证尚未 sync/close 的最新业务数据一定保存。掉电后可能丢失最后一次提交,但通常仍可挂载。

5. Flash 磨损方面为什么 LittleFS 更有优势

Flash 擦除次数有限,例如 NOR Flash 某个 erase block 可能只能承受数万到十万次擦除。

FAT 存在明显的热点区域:

  • FAT 表
  • 根目录或频繁更新的目录
  • FSInfo
  • 高频修改文件的目录项

即使文件数据分布在整个分区,少量关键元数据块也可能被反复重写。某个关键块先磨损,就可能造成严重损坏。

LittleFS 针对这种情况提供动态磨损均衡:

  • 文件数据从空闲块中循环分配
  • 元数据以追加方式写入,减少每次修改都擦除同一位置
  • 元数据经过一定更新次数后可以迁移到其他块
  • block_cycles 等参数可控制元数据迁移频率
  • 底层报告编程或擦除错误时,可以尝试放弃问题块并迁移

因此擦除次数会更均匀地分布,而不是长期集中在几个 FAT 热点块上。

但 LittleFS 不能替代:

  • NAND ECC
  • 坏块管理
  • 电压稳定设计
  • 正确的 Flash 驱动
  • 存储控制器 cache flush

6. 为什么只能说“概率更低”

LittleFS 仍可能挂载失败,例如:

  • 根元数据对的两个块都损坏
  • 驱动报告写成功,但实际上数据没有落盘
  • read_size、prog_size、block_size 配置错误
  • 固件升级后修改了 LittleFS 几何参数
  • Flash 出现无法被 ECC 修复的位错误
  • RAM 越界或错误地址写坏文件系统分区

另外,如果 FAT 位于 SD 卡、eMMC 或带 FTL 的设备上,底层控制器已经提供磨损均衡和一定的掉电保护,LittleFS 相对 FAT 的优势会缩小。

最终可以概括为:

FAT:多个位置原地更新,掉电时可能留下无法判断的中间状态。

LittleFS:先保留旧状态,写完整并通过 CRC 后再提交;
          掉电后通常可以回退到最后一个有效提交。

所以对裸 NOR Flash、频繁写入、可能随时复位的嵌入式设备,LittleFS 通常比标准 FAT 更不容易因文件系统结构损坏而挂载失败。

posted @ 2026-09-02 12:27  bk街头狂舞  阅读(38)  评论(0)    收藏  举报