发布时间:2026/8/25 11:51:05
LittleFS 嵌入式文件系统机制拆解与某 RISC-V MCU 实测配置 做嵌入式的同行大概都遇到过这种返修客户反馈设备用着用着配置丢了或者 OTA 升级中途断电后板子再也起不来。查下来根因十有八九落在 Flash 文件系统上——要么掉电写坏了 FAT 表要么某个扇区被写穿。FAT 不抗掉电裸写 Flash 不耐磨这俩坑在量产面前迟早要还。这篇文章拆解 LittleFS 怎么从根上解决这俩问题并拿某款 RISC-V MCU 的 SDK 实测配置做案例讲清楚每个配置项为什么这么填。一、为什么嵌入式需要专门的文件系统SPI NOR Flash 有三条铁律按页读写、按块擦除、写前必擦。页一般是 256 字节擦除块是 4096 字节而且擦写有寿命一块 Flash 擦写十万次左右就开始出坏块。FAT 在 PC 上没问题搬到嵌入式有三个硬伤。最致命的是掉电就损坏FAT 表和目录项是原地改写的写一半断电FAT 表和目录对不上整个分区就废了。再就是没有磨损均衡同一个目录反复改永远写那几个扇区很快就写穿。还有 RAM 占用随文件数增长小 MCU 扛不住。LittleFS 就是冲着这三点设计的掉电安全、动态磨损均衡、RAM/ROM 有界。它的状态严格受控RAM 消耗不随文件系统变大而涨也没有无界递归。二、LittleFS 的四个核心机制2.1 CTZ 压缩目录大目录不会写穿一个块CTZ 是 count-leading-zeros 的缩写听起来玄乎本质是一种可变长跳转的链表编码。LittleFS 的目录不是像 FAT 那样占一段连续扇区的目录表而是把每个目录条目组织成链表每个条目里存一个skip 值指向后面第几个块。关键在于这个 skip 值用 CTZ 编码后是变长的前面几个条目 skip 小、占的位少越往后 skip 越大、占的位多。结果是目录条目天然分散到多个块上不会全挤在一个块里。为什么要这样设计因为目录是写最频繁的元数据。FAT 的目录表集中在几个连续扇区你往一个目录里反复增删文件那几个扇区很快就被写穿了。CTZ 把目录摊开到多个块配合下面的磨损均衡目录块的磨损被均摊开。2.2 磨损均衡block_cycles 让元数据块轮换着用LittleFS 的写都是 copy-on-write要改一个块不是原地改而是写一个新块再把指针指过去。这本身就是磨损均衡的基础写不固定落在某个块。但光靠 copy-on-write 还不够。超级块和目录块是热点每次挂载要读超级块每次改目录要写目录块它们的擦写频率远高于数据块。如果任由它们待在原地迟早被写穿。所以 LittleFS 加了block_cycles机制一个元数据块每被擦写 N 次后就把它整个搬到另一个新块上旧块释放。本工程配的是 500意思是元数据块擦写 500 次就换一个家。官方对block_cycles的说法很直白建议 100-1000值大性能好但磨损分布不均匀值小磨损均匀但搬动开销大。500 是个偏中庸的选择。设成 -1 则关闭块级磨损均衡除非你确信自己的负载不会产生热点元数据块否则别关。延迟擦除是另一个细节旧块被新块取代后不立刻擦先标记失效等空闲了再擦避免擦除阻塞关键路径。2.3 掉电保护三步 copy-on-write 保证任意时刻可回退这是 LittleFS 最值钱的设计。一次写操作拆成三步写新块把新数据写到空闲块更新指针原子地把元数据指针从旧块指到新块释放旧块旧块标记为空闲任意时刻掉电文件系统都能回到上一个一致状态 - 掉在第 1 步新块写了一半指针还指着旧块旧数据完好新块那半截下次 GC 回收 - 掉在第 2 步指针要么没更新回退到旧块要么更新了用新块二选一不会乱 - 掉在第 3 步指针已经指新块了旧块没释放只是多了个垃圾块不影响正确性对比 FATFAT 表是原地改写的掉电时 FAT 表和目录可能各改了一半对不上就损坏。这是 FAT 在嵌入式不抗掉电的根因。源文档里有个实测很能说明问题往文件写两段数据但不 close直接拔电重新上电后读到的还是上一次成功 close 的内容不会读到半截损坏数据。LittleFS 保证回到上一个一致状态。2.4 元数据双副本单块损坏不影响挂载关键元数据挂载点、根目录写两份交替更新。这次写副本 A下次写副本 B再下次又写 A。两份里只要有一份是好的就能挂载。为什么需要双副本元数据是单点超级块坏了整个文件系统都认不出来。双副本把单点故障概率降一个数量级配合 CRC 校验一个副本损坏能被检测到并切到另一个副本。三、lfs_config 每个字段都在说什么这是新手最容易踩坑的地方。lfs_config不是随便填的每个字段都要和底层 SPI Flash 的物理参数对齐填错轻则性能差重则挂载失败、数据损坏。某 RISC-V MCU SDK 的实测配置适配层ak_lfs化名里长这样static struct lfs_config cfg { .read block_device_read, // 读XIP memcpy .prog block_device_prog, // 写spi_flash_write .erase block_device_erase, // 擦spi_flash_sector_erase .read_size 256, // Flash 页大小 .prog_size 256, // Flash 页大小 .block_size 4096, // Flash 扇区大小 .block_count 25, // 挂载时按分区大小重算 .cache_size 256, // RAM 缓存 .lookahead_size 16, // 块分配位图前瞻 .block_cycles 500, // 元数据磨损均衡周期 };逐个说。read_size / prog_size 256读和写的最小粒度必须等于 Flash 的页大小。SPI NOR Flash 一页 256 字节跨页读要发多条命令所以填页大小最经济。填小于页没意义底层还是按页读填大于页会要求底层做超出物理能力的对齐。block_size 4096擦除块大小等于 Flash 的扇区大小。官方注释强调这个值不影响 RAM 消耗但非内联文件至少占一整个块所以 block_size 越大小文件越浪费空间。它必须是 read_size 和 prog_size 的倍数。block_count文件系统的块数。这里填 25 只是占位挂载时适配层会按分区实际大小重算block_count 分区字节数 / 4096。填 0 也行LittleFS 会读磁盘上存的 block_count。cache_size 256RAM 里的块缓存大小。LittleFS 需要一个读缓存、一个写缓存外加每个打开的文件一个缓存。必须是 read_size/prog_size 的倍数且是 block_size 的因数。本工程填 256一页省 RAM想提速可以填大比如 4096一整块缓存命中率高但多吃 4KB RAM。lookahead_size 16块分配的前瞻缓冲。它是个紧凑位图1 字节能跟踪 8 个块。16 字节就能前瞻 128 个块。分配新块时一次扫一批比一块一块找快。值大找块快但多吃 RAM。block_cycles 500前面讲过的元数据块轮换周期。100-1000 之间取舍。记住一条这三个参数4096 / 256 / 256必须和生成文件系统镜像时的参数完全一致否则挂载失败。源文档里生成空镜像用的命令是mklfs -b 4096 -p 256 -r 256和配置里的 block_size/prog_size/read_size 一一对应。四、分区挂载LittleFS 不懂分区适配层来填这是个容易混淆的点LittleFS 本身没有分区概念。它只认一个从第 0 块到第 N 块的块设备不知道也不关心这些块在 Flash 上的物理位置。但实际产品里一块 SPI Flash 会被切成多个分区ROMFS 放只读资源、LittleFS 放可读写数据、还有 OTA 分区。LittleFS 只该管自己那段。把分区名映射到 Flash 物理位置是适配层的活。某 SDK 的适配层ak_lfs化名干的就是这件事挂载流程是ak_lfs_mount(UDATA) → partition_get_bin_info(binInfo) // 按UDATA查 flash 头部分区表 → lfs_info.start_page binInfo.file_start_page // 分区起始页 → cfg.block_count binInfo.file_len / 4096 // 按分区大小算块数 → lfs_mount(lfs, cfg) // 挂载分区表存在 Flash 头部第 0 页附近由烧录工具在烧录时写入。适配层按分区名字符串匹配返回该分区的起始页、长度、XIP 映射地址。读写路径还分两条读走 XIPeXecute In Place把 Flash 物理地址映射到 CPU 能直接 memcpy 的地址读不经过 SPI 命令开销写和擦走 spi_flash_write / spi_flash_sector_erase按页写、按扇区擦。读快写慢这跟 Flash 物理特性一致。为什么必须要有这层适配因为 LittleFS 的块设备接口是抽象的它说读第 K 块但第 K 块在 Flash 上的真实地址是分区起始页 K*4096这个换算只有适配层知道。没有适配层LittleFS 会从 Flash 第 0 块开始用直接踩到分区表和 ROMFS 上去。五、和 EasyFlash 比什么时候选谁先说清楚一个前提本 SDK 并没有集成 EasyFlash全仓搜不到任何 easyflash 源码。下面的对比是基于 EasyFlash 公开机制armink/EasyFlash v4.x的理论推演用来理解选型逻辑不是实测对比。两者定位完全不同。LittleFS 是文件系统有目录、文件、路径数据模型是path → bytesEasyFlash 是 KV 存储库核心是ef_env的key → value外加日志和 IAP数据模型是扁平的键值对。场景LittleFSEasyFlash存大量结构化数据文件、音频、OTA 包强弱KV 不适合大块二进制存少量配置键值对几十个参数能用但重强ef_env_set/get一行搞定需要目录/路径组织强无代码体积较大编译后约 20KB较小核心 env 几 KB本工程为什么选 LittleFS因为 OTA、音频、指纹这些要存的是文件用文件 APIopen/read/write/close语义统一上层代码能复用。而且锁类设备掉电频繁LittleFS 的 copy-on-write 保护任意文件比 EasyFlash 的 KV 备份更通用。本工程的配置参数走自有存储没用到 EasyFlash 的 env 模型引入 EasyFlash 反而多一层抽象。说到底就一条要存文件选 LittleFS要存几十个配置参数且追求极小体积选 EasyFlash 的 ef_env。六、实测一条 CLI 命令跑通 LittleFS源文档里在ez_ble_test.c化名新增了一条ez_lfsCLI 测试命令设计很简洁单命令形式为ez_lfs加操作符和参数用 static 状态机管理当前文件句柄和挂载标志。参考了 SDK 里已有的多命令测试但改成单命令形式省去注册一堆命令。基本读写测试跑下来是这样msh ez_lfs mount lfs mount success msh ez_lfs open test.txt msh ez_lfs write hello_lfs_123 lfs write success, len:13, pos:13 msh ez_lfs seek 0 0 msh ez_lfs read 32 lfs read success, len:13, data:hello_lfs_123 msh ez_lfs close掉电保护测试更有意思mount 后 open 一个文件写两段数据但不 close直接拔板子电源模拟写中断。重新上电 mount 后读读到的是上一次成功 close 的内容或空不会读到半截损坏数据。这正是 2.3 节讲的三步 copy-on-write 在起作用。实测踩了三个坑值得说。第一个是 UDATA 分区要单独烧默认烧录配置不含它mount 会因找不到分区失败得先用另一套烧录配置把 UDATA 分区和 lfs.bin 烧进去。第二个是写后必须 closelittlefs 的写在 close 或 sync 前不落盘写完直接断电数据不保证持久化掉电测试观察的正是这点。第三个是单 fd 设计命令只维护一个当前句柄open 新文件会先关旧文件多文件并发要自己扩展成句柄表。构建验证也印证了机制未加ez_lfs命令前因为开了--gc-sections且无人引用littlefs 库整个被链接器丢弃elf 里 0 个 lfs 符号加了命令把符号拉回后87 个 littlefs v2.50 符号全部链接进来bin 大了约 20KB即 lfs 库体积。写在最后选文件系统这事本质是在掉电安全、磨损均衡、资源占用之间取舍。LittleFS 用 copy-on-write 双副本 CTZ 目录把前两个做到了嵌入式能用的程度代价是 20KB 左右的代码体积和一点 RAM 缓存。对要存文件、会掉电的产品这笔账划算。你的产品上用什么文件系统掉电保护实测过吗还是只在实验室跑通就上了评论区聊聊有用的话点个在看让更多踩过 Flash 损坏坑的同行看到。嵌入式 #文件系统 #LittleFS #Flash存储 #单片机

相关新闻

2026/8/25 11:51:05

齿轮参数化设计:参数驱动、约束关联与模型自洽

1. 什么是齿轮参数化设计:从图纸堆里爬出来的工程师都懂的“救生索”你有没有经历过这样的场景:凌晨两点,CAD界面还亮着,手边是三张不同模数、压力角、齿数的齿轮图纸,而客户刚发来第四个修改需求——“把齿顶高系数从…

2026/8/25 11:46:05

测试工程师必备:基于Python的图片自动化测试工具集构建指南

1. 项目概述:为什么测试工程师需要一个专属的图片测试工具集?如果你是一名测试工程师,尤其是负责Web、移动端或客户端应用的功能、UI或兼容性测试,那么“图片”绝对是你绕不开的测试对象。从简单的图标显示、复杂的图表渲染&#…

2026/8/25 11:46:05

深度学习Dense层全解析:从参数调优到实战避坑指南

1. 项目概述:从“全连接”到“密集连接”的认知升级在深度学习的模型构建中,tf.keras.layers.Dense()几乎是每个入门者最早接触、也最频繁使用的层之一。很多人习惯性地称它为“全连接层”,这个叫法固然没错,但我觉得“密集连接层…

2026/8/25 14:26:43

企业架构的六种场景:从“四大流派”到数字原生与 AI 原生

前几天读到陈果老师的《企业架构的中国实践:四大流派与本质回归》,“四种流派”这个标题确实很吸引人。文章把国内的 EA 实践归为四类:原教旨主义 TOGAF 派、金融机构派、华为派与学华为派、实用价值派,并以“回归 EA 作为战略沟通…

2026/8/25 14:26:43

【嵌入式成长7】STC89C51数码管|共阴/共阳原理,74HC245、74HC595级联、动态扫描、定时器中断刷新实战

一、数码管基础简介 数码管可以理解为8个LED封装在一起,7段笔画a‑g加上小数点dp。点亮对应段LED就可以显示数字、部分字母。 数码管分两大类:共阴极、共阳极。类型公共端接法点亮逻辑共阴极全部LED阴极接GND段引脚输出高电平点亮笔画共阳极全部LED阳极接…

2026/8/25 14:26:43

初识linux(day 06)

今天介绍的有以下五个内容: 1.printf 的缓冲区问题 2.静态库的创建和使用 3.共享库的创建和使用 4.静态库与共享库的区别 5.各种命令,库等文件的标准存放位置一,printf 的缓冲区问题 printf ( )的输出原理:将需要打印在屏幕上的东…

2026/8/25 14:26:43

dede php做网站?别被忽悠了!网页和网站差个十万八千里

对很多人而言, 对于网页究竟是什么, 一下子就处于那种完全摸不着头脑好似丈二和尚一般的状态。那网页到底是什么? 是效果图? 是代码? 是编程? 我表示 , 网页其实是它们融合而成的综合体 , 它里面的任何单独一项 , 都没办法称其为完整的情况。要是想要学会网页相关知识 , 那…

2026/8/25 14:26:43

2026-08-24:检查相邻数字差。用go语言,给定一个纯数字字符串,你需要检查其中任意两个紧挨着的数字,它们数值上的差距是否都不会超过 2。 如果整串数字中所有相邻位置都满足这个条件,就返回“是”

2026-08-24:检查相邻数字差。用go语言,给定一个纯数字字符串,你需要检查其中任意两个紧挨着的数字,它们数值上的差距是否都不会超过 2。 如果整串数字中所有相邻位置都满足这个条件,就返回“是”(true&…

2026/8/25 14:21:42

RAG的工作流程与目标

目录 RAG的工作流程: 知识库的构建: 调用: RAG的目标:如何提高Query和知识库的匹配度 如何优化Query: 如何优化知识库: 扩展:Embedding模型 RAG的工作流程: 上一节…

2026/8/25 1:04:19

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/25 11:48:27

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/24 8:17:29

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/25 0:04:14

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory Meta Description:GetQzonehistory 是一个QQ空间历史说…

2026/8/25 0:04:14

洛谷 P7912:[CSP-J 2021 T4] 小熊的果篮 ← 双向链表

【题目来源】 https://www.luogu.com.cn/problem/P7912 【题目描述】 小熊的水果店里摆放着一排 n 个水果。每个水果只可能是苹果或桔子,从左到右依次用正整数 1,2,…,n 编号。连续排在一起的同一种水果称为一个“块”。小熊要把这一排水果挑到若干个果篮里&#x…

2026/8/24 13:42:17

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/24 18:13:48

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/25 1:08:14

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…