设想一台靠电池供电的记录仪,正在写入最新的测量值,供电线却被人拔掉。重新启动后,文件还能读取,与最后那条测量记录还在,是两个层面的成功。开源 C 语言文件系统 littlefs 的设计目标,是在写入中断后恢复到有效状态。要知道这个状态里保留了什么,就要沿着数据块、元数据和最终提交,追踪一次更新的全过程。[1]
这里讨论的存储,就在一块小小的电路板上。树莓派 2024 年 12 月的 Pico Python SDK 指南记载,初代 Pico 的板载闪存默认划出 1600 kB 文件系统,使用 littlefs2。[6] 一次熟悉的 Python 文件操作,底下依靠的正是一套为写到中途突然断电而设计的存储机制。
更新过程中,始终留下一份可用记录
闪存覆盖旧内容受到限制:编程写入的单位小于擦除的单位。空间用到一定程度,要再次使用,就得擦除整个块。littlefs 将短小的元数据日志与文件内容的写时复制(copy-on-write)结合起来,适应写入与擦除单位的差异。[3]
元数据记录文件名、文件内容所在位置等信息。littlefs 用成对的块存放这些信息。在一个块内,历次提交不断向日志末尾追加,以新条目为准,旧条目则暂时保留。每次提交都以 CRC(循环冗余校验)收尾,这个校验值帮助识别中途打断或受损的写入。[2]
当第一个块需要回收时,配对的第二个块就派上了用场。文件系统将仍然有效的信息紧凑地整理到另一个块中,同时保留原先那份可用记录。替换完成后,旧块便可以重新使用。[3] 如果两个块都含有有效提交,修订计数器负责辨认哪个块更新;校验和验证则区分完整提交与尾部残留的无效数据。[2]
这里有两项独立的工作:辨认哪份记录更新,确认这份记录是否写完。修订号更高,也仍要验证提交的完整性。
文件内容走另一条路
较大的文件使用写时复制的数据块:变更后的内容先写入替换存储空间,随后元数据才转而指向新的存储表示。littlefs 用向后链接的 CTZ 跳表组织这些块。向后的链接使追加写入能够沿用前面的块,额外的链接则缩短遍历过程。修改文件中部时,有时还要复制后续块,因此追加一条测量记录与修改旧文件头,开销也会有差别。[3]
小文件还可以作为内联文件,直接存放在元数据日志中。[2] 极小的值因此也有了与元数据共用存储空间的方式。无论采用哪种方式,都要弄清楚:新内容在何时才成为已经提交的内容。
两层同步必须衔接
在应用程序这一层,lfs_file_write() 返回写入字节数或错误。API 文档说明,文件要等到同步或关闭时,才会更新到存储介质上。lfs_file_sync() 将待写入的变更写出,lfs_file_close() 则在释放资源前完成这项工作。两者都能报告失败。[4]
文件系统下面还有一层接口。集成者需要为存储设备提供 read、prog、erase 和 sync 回调。[4] 项目的 README 明确要求,底层 sync 必须将缓存的写入刷到存储介质,并确保后续读取访问底层存储。如果驱动一边报告完成,一边仍将关键写入留在易失性内存中,上层所依赖的持久性前提就会失效。[1]
对于这台设想中的记录仪,“已接收测量值”和“已提交测量值”应当是两个独立事件。显示屏如果提示读数已经保存,这项承诺就应当在同步成功之后给出。应用程序还要决定,同步失败时怎样处理。
文件系统完好,任务仍会停在半途
现在让记录仪使用两个文件:一个保存测量记录,另一个保存指向最新记录的索引。假设程序已经提交测量文件,却在提交索引之前断电,就会出现两个文件各自有效、索引仍落后于测量记录的情况。
这个例子揭示了保证的范围。littlefs 文档列出了重命名、删除等原子操作,也说明文件会在同步或关闭时提交。[1] 记录仪对两个文件的这串操作,仍需应用程序另作安排才能成为一个事务。问题来自应用程序如何划定一次任务的范围。
一种设计是让索引能够依据测量记录重建。另一种设计是把必须一起变更的信息放进同一个提交对象。这些选择属于应用程序,决定了文件系统完成自身职责之后,恢复究竟应当达到什么状态。
算清内存,再选择用途
RAM 的用量有明确的配置依据。cache_size 设置读取缓存、编程写入缓存,以及每个已打开文件的额外缓存大小。lookahead_size 控制分配空间时使用的位图大小,其中每个字节追踪八个块。[4] 因此,打开更多文件会改变内存预算;仅仅扩大文件系统容量,内存预算则可以保持原样。
乐鑫的独立集成指南将 littlefs 列为通用文件系统选项之一,同时指出它缺乏与其他平台的原生兼容性。同一份指南建议将小型配置和校准记录交给 NVS 键值存储。[5] 应用需要文件时,文件树很有用;需求若只有寥寥几个设置项,文件树就成了额外的一层维护工作。
对于有能力维护闪存驱动、在自己的硬件上测试恢复过程的小型固件团队,这套架构划出了具体的职责分工。littlefs 负责让文件系统更新后仍可恢复,团队负责定义一项任务怎样才算完成。供电线拔掉之后,决定性的问题是:记录仪能否解释清楚自己恢复到了什么状态。
来源
- littlefs 项目,README——恢复保证、C 语言实现、原子操作、文件提交,以及存储驱动的 sync 约定;查阅于 2026 年 10 月 30 日。
- littlefs 项目,技术规范,盘上格式版本 2.1——元数据块对、修订计数器、提交校验和与内联文件;查阅于 2026 年 10 月 30 日。
- littlefs 项目,《The design of littlefs》(littlefs 的设计)——闪存限制、元数据紧凑整理、写时复制存储和 CTZ 跳表;查阅于 2026 年 10 月 30 日。
- littlefs 项目,公开 API 头文件 lfs.h——块设备回调、缓存配置,以及文件写入、同步和关闭的约定;查阅于 2026 年 10 月 30 日。
- 乐鑫,《File System Considerations》(文件系统选型考量),适用于 ESP32-P4 的 ESP-IDF v5.5 编程指南——从独立集成方的角度介绍 littlefs 与 NVS。
- Raspberry Pi Ltd,《Raspberry Pi Pico-series Python SDK》,2024 年 12 月 4 日构建版本,第 3 章,第 13 页——默认使用 littlefs2 的 Pico 文件系统。
- Michael H.(“Laserlicht”),《Raspberry pi pico (cropped) top》,维基共享资源——照片摄于 2021 年 1 月 27 日,Elmepi 于 2022 年裁剪;CC BY-SA 4.0。