NVS 原理与恢复行为
本页依据 文档基线与后续更新 中记录的 Zephyr main 快照更新。
NVS 通过顺序追加、元数据提交和循环回收减少 Flash 擦除次数。 本页描述当前存储算法的关键约束;私有字段和控制流程仍需对应具体提交阅读。 公共使用方法见 NVS 非易失键值存储。
sector 中的两条增长方向
数据从 sector 前部向后追加,allocation table entry(ATE)从末尾向前追加。 一条记录包含载荷及元数据,元数据描述 ID、数据偏移、长度等信息,并带元数据 CRC。 基础元数据逻辑大小为 8 字节,物理占用还需按设备写块对齐。
图是早期教程中的概念图。当前字段和实际布局见本页列出的固定提交源码。 ID 的高值有内部用途,不应把 16 位 ID 范围全部当成可用的应用键。
提交、读取与删除
写入先追加数据,再写入有效元数据。只有数据、缺少完整有效 ATE 的区域在挂载恢复时 不会被当成成功提交的新值。元数据 CRC 用于识别未完成写入, 可选数据 CRC 则用于载荷校验,两个作用不能互换。
读取沿记录寻找相同 ID 的最新有效版本。写入前会比较已有值,相同 ID 的内容未改变时 避免再次写 Flash。删除使用零长度记录表达;旧载荷并不会因删除立即物理擦除, 因此删除 API 本身不构成安全擦除承诺。
多个 ID 的连续更新是多次独立写入,不构成跨 ID 的事务。 需要原子更新一组参数时,可把它们放进一条带版本号的数据记录,或在应用中设计提交协议。
sector 切换与垃圾回收
当前 sector 容量不足时,NVS 进入循环中的下一位置,并处理仍有效的记录, 使即将被擦除区域中的最新数据能够保留下来。 至少保留一个空闲 sector,才能有空间复制和继续更新。
有效数据集过大或可用空间过少,会造成回收压力。实现会避免无休止的擦除循环并返回空间
不足错误,应用不能只靠重试解决容量问题。
nvs_sector_use_next() 可强制切换,但频繁使用会增加擦除,
不适合作为每次写入后例行调用的“提交”接口。
挂载与中断恢复
nvs_mount() 检查设备写块、sector 布局与数量,再扫描已有记录和 sector 标记,
恢复追加位置与有效数据。记录完整性依赖介质与驱动遵守写入、擦除语义。
应用仍需验证自己的记录版本、长度、数值范围和跨版本兼容性。 “能够恢复最后完整的 NVS 记录”不意味着任意断电位置下,所有应用状态都自动满足业务一致性。 升级改变数据 CRC、分区大小、sector 组织或写块大小时,应先确认旧数据的可读性。
磨损估算
可用如下粗略关系估算擦除寿命:
寿命(分钟)≈ sector 数量 × sector 大小 × 单页允许擦除次数
/ [每分钟写入次数 × (记录数据长度 + 元数据开销)]
实际还包含对齐、CRC、sector 标记、垃圾回收搬移、有效数据占用和不均匀布局等开销, 这个关系不能替代器件规格与负载测量。减少不必要更新、聚合变化、增加可用空间, 通常比频繁强制擦除更适合延长寿命。
源码阅读顺序
从公共 nvs_mount/read/write/delete 接口开始,再查看 subsys/kvss/nvs/nvs.c
中的挂载、追加与 GC 路径,以及 nvs_priv.h 的 ATE 与地址布局。
旧教程中的 subsys/fs/nvs 路径已经迁移。