LittleFS 小型文件系统(一)
参考文章:
嵌入式Flash文件系统LittleFS,用过都说好! - 知乎
LittleFS:一个完整的嵌入式文件系统介绍、移植使用教程-CSDN博客
小型文件系统(littlefs) - fire909090 - 博客园
LittleFs文件系统 - 简书
1、介绍
源码下载地址:https://github.com/littlefs-project/littlefs/tags
LittleFS有ARM官方发布,适合单片机使用,具有三个突出的优点。
1、掉电保护,在操作时断电也能恢复到一个正确的状态
2、动态磨损均衡,有效延长FLASH的寿命
3、低资源消耗,有限的RAM\ROM
2、移植
3、接口列表
1、参数接口
1)文件类型
// File types
enum lfs_type {// file typesLFS_TYPE_REG = 0x001, 普通文件LFS_TYPE_DIR = 0x002, 目录// internally used typesLFS_TYPE_SPLICE = 0x400, 文件修改操作LFS_TYPE_NAME = 0x000, 文件名记录LFS_TYPE_STRUCT = 0x200, 结构化数据LFS_TYPE_USERATTR = 0x300, 用户自定义属性LFS_TYPE_FROM = 0x100, 表示来源信息LFS_TYPE_TAIL = 0x600, 尾部记录LFS_TYPE_GLOBALS = 0x700, 全局变量记录LFS_TYPE_CRC = 0x500, CRC校验计算// internally used type specializationsLFS_TYPE_CREATE = 0x401, 创建新文件/目录LFS_TYPE_DELETE = 0x4ff, 删除文件/目录LFS_TYPE_SUPERBLOCK = 0x0ff, 文件系统元信息LFS_TYPE_DIRSTRUCT = 0x200, 目录结构LFS_TYPE_CTZSTRUCT = 0x202, CTZ压缩指针结构LFS_TYPE_INLINESTRUCT = 0x201, 内联结构LFS_TYPE_SOFTTAIL = 0x600, 软连接尾部LFS_TYPE_HARDTAIL = 0x601, 硬链接尾部LFS_TYPE_MOVESTATE = 0x7ff, 移动操作的状态记录LFS_TYPE_CCRC = 0x500, 提交操作的CRCLFS_TYPE_FCRC = 0x5ff, 文件结束的CRC// internal chip sourcesLFS_FROM_NOOP = 0x000, 空操作LFS_FROM_MOVE = 0x101, 该记录由文件移动产生LFS_FROM_USERATTRS = 0x102, 该记录由用户设置的扩展属性操作产生
};
2)文件打开标志
// File open flags
enum lfs_open_flags {LFS_O_RDONLY = 1, // 只读方式打开文件LFS_O_WRONLY = 2, // 只写模式打开文件LFS_O_RDWR = 3, // 读写模式打开文件LFS_O_CREAT = 0x0100, // 如果文件不存在,创建文件LFS_O_EXCL = 0x0200, // 文件已存在,则失败LFS_O_TRUNC = 0x0400, // 将现有文件截断至0LFS_O_APPEND = 0x0800, // 每次写入时都移动到文件末尾LFS_F_DIRTY = 0x010000, // 文件内容与存储内容不一致LFS_F_WRITING = 0x020000, // 文件至上次刷新后已经写入过内容LFS_F_READING = 0x040000, // 上次刷新后,文件已经被读取LFS_F_ERRED = 0x080000, // 写入过程中出现错误LFS_F_INLINE = 0x100000, // 当前已经嵌入到目录中
};
3)文件查找标志
// File seek flags
enum lfs_whence_flags {LFS_SEEK_SET = 0, // 相对于绝对位置查找LFS_SEEK_CUR = 1, // 根据文件当前位置查找LFS_SEEK_END = 2, // 相对文件末尾位置查找
};
4)文件系统配置结构体
// Configuration provided during initialization of the littlefs
struct lfs_config {void *context; 传递给block驱动代码的上下文int (*read)(const struct lfs_config *c, lfs_block_t block,lfs_off_t off, void *buffer, lfs_size_t size); 读取回调函数int (*prog)(const struct lfs_config *c, lfs_block_t block,lfs_off_t off, const void *buffer, lfs_size_t size); 写入回调函数int (*erase)(const struct lfs_config *c, lfs_block_t block); 擦除回调函数int (*sync)(const struct lfs_config *c); 同步块设备的状态int (*lock)(const struct lfs_config *c); 设备加锁回调函数int (*unlock)(const struct lfs_config *c); 设备解锁回调函数lfs_size_t read_size; 最小的读取单元大小lfs_size_t prog_size; 最小的写入单元大小lfs_size_t block_size; 最小的擦除单元大小,可以比flash的实际block尺寸大。但是对于ctz类型的文件,
block size是最小的分配单元。block_size必须是read_size和prog_size大小的倍数 lfs_size_t block_count; FLASH设备的块数量int32_t block_cycles; 文件系统进行垃圾回收时block的擦除次数,推荐值100-1000.值越大垃圾回收次数越少lfs_size_t cache_size; lfs需要read cache、program cache每个文件也需要cache,
cache越大性能越好,会减少flash的访问次数,chche是读写大小的倍数,是擦除大小的因数 lfs_size_t lookahead_size; 前瞻缓冲区的大小,增加了分配块时找到块的数量lfs_size_t compact_thresh; 用于压缩元数据的阈值void *read_buffer; 读缓冲区 可选动/静态分配void *prog_buffer; 写缓冲区 可选动/静态分配void *lookahead_buffer; 预读缓冲区 可选动/静态分配lfs_size_t name_max; 文件名的最大长度lfs_size_t file_max; 文件的最大长度lfs_size_t attr_max; 用户属性的最大长度lfs_size_t metadata_max; 用于存储元数据的空间上限lfs_size_t inline_max; 内联文件大小上限uint32_t disk_version; 版本号};
5)文件信息结构
// File info structure
struct lfs_info {uint8_t type; 文件的类型 LFS_TYPE_REG or LFS_TYPE_DIRlfs_size_t size; 文件大小 仅适用于REG文件char name[LFS_NAME_MAX+1]; 文件名
};
6)文件系统结构信息
// Filesystem info structure
struct lfs_fsinfo {uint32_t disk_version; 磁盘版本lfs_size_t block_size; 逻辑块的大小lfs_size_t block_count; 块数量lfs_size_t name_max; 文件名长度上限lfs_size_t file_max; 文件大小上限 lfs_size_t attr_max; 自定义属性上限
};
7)用户属性
struct lfs_attr {uint8_t type; 属性类型 由用户提供void *buffer; 缓冲区 lfs_size_t size; 属性大小
};
8)文件可选配置
struct lfs_file_config {void *buffer; cache size长度的Buffer,可动静态分配struct lfs_attr *attrs; 用户属性,读文件时,attr存储从flash上读取的文件用户属性
写文件时,arrt存放用户指定的文件属性写入到flash中lfs_size_t attr_count; 用户属性的长度
};
9)文件系统数据结构
typedef struct lfs_cache {lfs_block_t block; 当前缓存对应的物理块 cache中的数据属于的blocklfs_off_t off; cache中的数据在block上偏移地址 lfs_size_t size; cache的大小uint8_t *buffer; cache数据的存放底子
} lfs_cache_t;
10)目录结构
typedef struct lfs_mdir {lfs_block_t pair[2]; 记录当前目录所在的两个物理块(主+次)uint32_t rev; 记录当前目录的版本号lfs_off_t off; 当前目录块中下一个可用位置的偏移量uint32_t etag; 最后一个目录项的结束标签uint16_t count; 当前目录中的目录项数bool erased; 标记该目录是都已经被擦除bool split; 标记该目录是否已经被拆分lfs_block_t tail[2]; 当目录分裂时 指向下一个目录块的地址
} lfs_mdir_t;
11 )文件系统目录类型
// littlefs directory type
typedef struct lfs_dir {struct lfs_dir *next; 指向下一个结点uint16_t id; 编号uint8_t type; 类型 LFS_TYPE_DIRlfs_mdir_t m; 目录原信息lfs_off_t pos; 当前在目录中的偏移位置lfs_block_t head[2]; 当前目录使用的两个块编号、用于日志结构切换
} lfs_dir_t;
12)文件系统文件类型
// littlefs file type
typedef struct lfs_file {struct lfs_file *next; 指向下一个文件对象uint16_t id; IDuint8_t type; 文件类型lfs_mdir_t m; 文件元信息struct lfs_ctz {lfs_block_t head; 文件内容起始块编号、使用CTZ压缩指针结构lfs_size_t size; 文件大小} ctz; uint32_t flags; 标志位、只读只写lfs_off_t pos; 当前文件的位置lfs_block_t block; 正在访问的Flash块编号lfs_off_t off; 当前块内的偏移量lfs_cache_t cache; 缓存结构const struct lfs_file_config *cfg; 用户配置指针
} lfs_file_t;LittleFS使用一种叫做CTZ的压缩指针结构来高效管理大文件
文件数据被分散在多个FLASH快中,每个块通过ctz.head链接在一起
13)FS超级块信息结构
typedef struct lfs_superblock {uint32_t version; 文件系统版本号lfs_size_t block_size; 每个块的大小,通常是Flash的擦除粒度,如4KBlfs_size_t block_count; 文件系统使用的总块数,决定了文件系统的大小lfs_size_t name_max; 文件名的最大长度lfs_size_t file_max; 文件最大大小lfs_size_t attr_max; 扩展属性的最大长度
} lfs_superblock_t;超级块的作用:
1.文件系统入口:LittleFS启动时会首先读取超级块的内容,根据其中信息重建文件系统状态,CRC校验失败,则尝试使用备份超级块
2. 配置信息存储告诉文件系统:我用的是多大的块?总共多少块?最大文件名长度是多少?这些信息决定了文件系统的运行行为和兼容性
3. 支持A/B更新机制LittleFS会在两个超级块之间切换写入防止因断电导致元数据损坏提高掉电安全性
14)全局管理
文件系统重用于管理全局状态
typedef struct lfs_gstate {uint32_t tag; 标签字段 包含操作类型 CRC校验信息lfs_block_t pair[2]; 两个块编号,用于表示操作涉及的源和目标位置
} lfs_gstate_t;tag是一个符合字段,由多个位域组成操作类型、版本号、CRC校验值、是否移动操作
15)文件系统类型
// The littlefs filesystem type
typedef struct lfs {lfs_cache_t rcache; // read cache 减少实际从闪存中读取的次数lfs_cache_t pcache; // program cache 将多个小写入合并为一次写入 提高效率lfs_block_t root[2]; // 记录根目录的两个物理块 (主块 是当前正在使用的块,存储最新的元数据和文件内容)// (次块 是主块的一个副本,用于实现原子性和断电恢复)// 元数据链表,用于跟踪所有打开的目录或文件的元数据struct lfs_mlist {struct lfs_mlist *next;uint16_t id; // 目录或文件的IDuint8_t type; // 类型 LFS_TYPE_DIR 或 LFS_TYPE_FILElfs_mdir_t m; // 目录项结构} *mlist;uint32_t seed; // 用于生成随机数 ,分配新块是避免冲突// 全局状态lfs_gstate_t gstate; // 当前活跃的全局状态lfs_gstate_t gdisk; // 成功写入磁盘的全局状态lfs_gstate_t gdelta; // 增量状态// 预加载机制 用于预加载某些块的数据,提升查找效率struct lfs_lookahead {lfs_block_t start; 当前预加载区域在Flash上的起始块编号lfs_block_t size; 预加载区域的大小lfs_block_t next; 下一个要加载的块编号lfs_block_t ckpoint; 检查块编号,用于断电恢复时定位uint8_t *buffer; 指向预加载数据的缓冲区} lookahead;const struct lfs_config *cfg; 文件系统配置lfs_size_t block_count; 总块数lfs_size_t name_max; // 最大文件名长度lfs_size_t file_max;// 最大文件大小lfs_size_t attr_max; // 最大属性长度lfs_size_t inline_max;// 内联数据的最大大小#ifdef LFS_MIGRATEstruct lfs1 *lfs1; 用于旧版本迁移
#endif
} lfs_t;