1、littlefs主要用在微控制器和flash上,是一种嵌入式文件系统。主要有3个特点:
1)、掉电恢复
在写入时即使复位或者掉电也可以恢复到上一个正确的状态。
2)、擦写均衡
有效延长flash的使用寿命
3)、有限的RAM/ROM
节省ROM和RAM空间
2、已有的文件系统
1)非掉电恢复,基于block的文件系统,常见的有FAT和EXT2。这两个文件系统在写入文件时是原地更新的,不具备非掉电恢复的特性。
2) 日志式的文件系统,比如JFFS,YAFFS等,具备掉电恢复的特性。但是这几个系统消耗了太多的RAM,且性能较低。
3) EXT4和COW类型的btrfs具有良好的恢复性和读写性能,但是需要的资源过多,不适合小型的嵌入式系统。
littlefs综合了日志式文件系统和COW文件系统的优点。从sub-block的角度来看,littlefs是基于日志的文件系统,提供了metadata的原子更新;从super-block的角度,littlefs是基于block的COW树。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
移植LittleFs
MT2503的板子外挂8Mbit的SPI Flash,本打算移植Fatfs,但Fatfs并不支持Wear leveling,后发现LittleFs,一个专门为单片机设计的文件系统,并且支持fail-safe。
详细介绍:https://os.mbed.com/blog/entry/littlefs-high-integrity-embedded-fs/
LittleFs Github https://github.com/ARMmbed/littlefs
LittleFs的移植很简单,只需实现Flash的读、写和擦除基本操作即可,官方例子如下:
-
#include "lfs.h"
-
-
// variables used by the filesystem
-
lfs_t lfs;
-
lfs_file_t file;
-
-
// configuration of the filesystem is provided by this struct
-
const struct lfs_config cfg = {
-
// block device operations
-
.read = user_provided_block_device_read,
-
.prog = user_provided_block_device_prog,
-
.erase = user_provided_block_device_erase,
-
.sync = user_provided_block_device_sync,
-
-
// block device configuration
-
.read_size = 16,
-
.prog_size = 16,
-
.block_size = 4096,
-
.block_count = 128,
-
.lookahead = 128,
-
};
-
-
// entry point
-
int main(void) {
-
// mount the filesystem
-
int err = lfs_mount( &lfs, &cfg);
-
-
// reformat if we can't mount the filesystem
-
// this should only happen on the first boot
-
if (err) {
-
lfs_format( &lfs, &cfg);
-
lfs_mount( &lfs, &cfg);
-
}
-
-
// read current count
-
uint32_t boot_count = 0;
-
lfs_file_open( &lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
-
lfs_file_read( &lfs, &file, &boot_count, sizeof(boot_count));
-
-
// update boot count
-
boot_count += 1;
-
lfs_file_rewind( &lfs, &file);
-
lfs_file_write( &lfs, &file, &boot_count, sizeof(boot_count));
-
-
// remember the storage is not updated until the file is closed successfully
-
lfs_file_close( &lfs, &file);
-
-
// release any resources we were using
-
lfs_unmount( &lfs);
-
-
// print the boot count
-
printf("boot_count: %d\n", boot_count);
-
}
已经实现的Flash驱动声明:
-
void flash_read_block(kal_uint32 addr, kal_uint8 *buff, kal_uint32 len);
-
void flash_write_block(kal_uint32 addr, kal_uint8 *buff, kal_uint32 len);
-
void flash_erase_block(kal_uint32 addr);
操作接口配置:
-
static int _block_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)
-
{
-
flash_read_block(block * c->block_size + off, (kal_uint8 *)buffer, (kal_uint32)size);
-
return 0;
-
}
-
static int _block_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size)
-
{
-
flash_write_block(block * c->block_size + off, (kal_uint8 *)buffer, (kal_uint32)size);
-
return 0;
-
}
-
static int _block_erase(const struct lfs_config *c, lfs_block_t block)
-
{
-
flash_erase_block(block * c->block_size);
-
return 0;
-
}
-
static int _block_sync(const struct lfs_config *c)
-
{
-
return 0;
-
}
-
-
const struct lfs_config cfg = {
-
// block device operations
-
.read = _block_read,
-
.prog = _block_prog,
-
.erase = _block_erase,
-
.sync = _block_sync,
-
-
// block device configuration
-
.read_size = 16,
-
.prog_size = 16,
-
.block_size = 4096,
-
.block_count = 256,
-
.lookahead = 256,
-
};
编译时出现大量Error,经过折腾最终搞定。
总结了一下:
1. armcc默认不支持对结构体指定成员名称进行初始化、不支持可执行代码之后声明变量,CFLAG添加 –gnu 可以解决问题,添加方法请参考《MTK功能机MMI,添加GNU特性》;
2. LittleFs需要提供系统的malloc, free函数接口。
原文地址:http://www.noblock.cn/?p=183
-------------------------------------------------------------------------------------------------------------------------------------------------------------
littlefs 重要的数据结构
1、文件类型
-
// File types -
enum lfs_type { -
// file types -
LFS_TYPE_REG = 0x001, -
LFS_TYPE_DIR = 0x002, -
// internally used types -
LFS_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, -
// internally used type specializations -
LFS_TYPE_CREATE = 0x401, -
LFS_TYPE_DELETE = 0x4ff, -
LFS_TYPE_SUPERBLOCK = 0x0ff, -
LFS_TYPE_DIRSTRUCT = 0x200, -
LFS_TYPE_CTZSTRUCT = 0x202, -
LFS_TYPE_INLINESTRUCT = 0x201, -
LFS_TYPE_SOFTTAIL = 0x600, -
LFS_TYPE_HARDTAIL = 0x601, -
LFS_TYPE_MOVESTATE = 0x7ff, -
// internal chip sources -
LFS_FROM_NOOP = 0x000, -
LFS_FROM_MOVE = 0x101, -
LFS_FROM_USERATTRS = 0x102, -
};
2、文件打开时的标志
-
// File open flags -
enum lfs_open_flags { -
// open flags -
LFS_O_RDONLY = 1, // Open a file as read only -
LFS_O_WRONLY = 2, // Open a file as write only -
LFS_O_RDWR = 3, // Open a file as read and write -
LFS_O_CREAT = 0x0100, // Create a file if it does not exist -
LFS_O_EXCL = 0x0200, // Fail if a file already exists -
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size -
LFS_O_APPEND = 0x0800, // Move to end of file on every write -
// internally used flags -
LFS_F_DIRTY = 0x010000, // File does not match storage -
LFS_F_WRITING = 0x020000, // File has been written since last flush -
LFS_F_READING = 0x040000, // File has been read since last flush -
LFS_F_ERRED = 0x080000, // An error occured during write -
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry -
LFS_F_OPENED = 0x200000, // File has been opened -
};
3、文件seek时的标志
-
// File seek flags -
enum lfs_whence_flags { -
LFS_SEEK_SET = 0, // Seek relative to an absolute position -
LFS_SEEK_CUR = 1, // Seek relative to the current file position -
LFS_SEEK_END = 2, // Seek relative to the end of the file -
};
4、lfs的配置参数
-
// Configuration provided during initialization of the littlefs -
struct lfs_config { -
// Opaque user provided context that can be used to pass -
// information to the block device operations -
/* 这个参数主要是传递给block驱动代码 */ -
void *context; -
/* 从设备读数据 */ -
int (*read)(const struct lfs_config *c, lfs_block_t block, -
lfs_off_t off, void *buffer, lfs_size_t size); -
/* 向设备写入数据,block设备在写入前必须已经erase了 */ -
int (*prog)(const struct lfs_config *c, lfs_block_t block, -
lfs_off_t off, const void *buffer, lfs_size_t size); -
/* 擦除block */ -
int (*erase)(const struct lfs_config *c, lfs_block_t block); -
/* sync块设备的状态 */ -
int (*sync)(const struct lfs_config *c); -
/* 最小的读取单元大小 */ -
lfs_size_t read_size; -
/* 最小的写入数据单元大小,也是数据metadata pair中tag的对齐尺寸 */ -
lfs_size_t prog_size; -
/* 最小的擦除单元大小。可以比flash的实际block尺寸大。但是对于ctz类型的文件,block size是最小的分配单元。同时block size必须是 -
read size和program size的倍数,block size会存储在superblock中 */ -
lfs_size_t block_size; -
/* 属于文件系统的block数量,block count会存储在superblock中 */ -
lfs_size_t block_count; -
/* 文件系统进行垃圾回收时的block的擦除次数,推荐取值100-1000.值越大垃圾回收的次数越少,性能越好 */ -
int32_t block_cycles; -
/* littlefs需要一个read cache,一个program cache,每个文件也需要一个cache。cache越大性能越好,会减少会flash的访问次数, -
cache必须是block的read size和program size的倍数,同时是block size的因数 */ -
lfs_size_t cache_size; -
/* lookahead buffer的尺寸。lookahead buffer主要是block alloctor在分配块的时候用到。lookahead size必须是8的倍数, -
因为它是采用bitmap的形式存储的 */ -
lfs_size_t lookahead_size; -
/* cache size大小的read buffer,可以静态分配也可以动态分配 */ -
void *read_buffer; -
/* cache size大小的program buffer,可以静态分配也可以动态分配 */ -
void *prog_buffer; -
/* lookahead_size大小的lookahead buffer,且是32-bit对齐的,即可以静态分配也可以动态分配 */ -
void *lookahead_buffer; -
/* 文件名的最大长度,这个值会存储在superblock中 */ -
lfs_size_t name_max; -
/* 文件的最大长度,存储在superblock中 */ -
lfs_size_t file_max; -
/* 用户属性的最大长度 */ -
lfs_size_t attr_max; -
};
5、文件信息
-
// File info structure -
struct lfs_info { -
// Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR 普通文件或者目录 -
uint8_t type; -
// Size of the file, only valid for REG files. Limited to 32-bits. 对于普通文件才有意义 -
lfs_size_t size; -
// Name of the file stored as a null-terminated string. Limited to -
// LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to -
// reduce RAM. LFS_NAME_MAX is stored in superblock and must be -
// respected by other littlefs drivers. -
/* 字符串形式的文件名 */ -
char name[LFS_NAME_MAX+1]; -
};
6、用户属性
-
struct lfs_attr { -
/* 属性类型 */ -
uint8_t type; -
/* 存储属性的buffer */ -
void *buffer; -
/* 属性的长度,最大值为LFS_ATTR_MAX */ -
lfs_size_t size; -
};
7、文件open时的配置
-
struct lfs_file_config { -
/* cache size长度的buffer,可以静态分配也可以动态分配 */ -
void *buffer; -
/* 用户属性,读文件时,attr存储从flash上读取的文件用户属性,写入文件时,attr存放用户指定的文件属性并会写入到flash中 */ -
struct lfs_attr *attrs; -
/* 用户属性的长度 */ -
lfs_size_t attr_count; -
};
8、lfs_cache结构
-
typedef struct lfs_cache { -
lfs_block_t block; // cache中的数据属于的block -
lfs_off_t off; // cache中的数据在block上的偏移地址 -
lfs_size_t size; // cache的大小 -
uint8_t *buffer; // cache数据的存放地址 -
} lfs_cache_t;
9、lfs_mdir结构,代表metadata pair,dir本身所在的block
-
typedef struct lfs_mdir { -
lfs_block_t pair[2]; // dir的metadata pair所在的block -
uint32_t rev; // metadata pair的revision -
lfs_off_t off; // tag的偏移地址 -
uint32_t etag; -
uint16_t count; -
bool erased; -
bool split; // metadata pair是否是链表 -
lfs_block_t tail[2]; // 用于metadata pair链表 -
} lfs_mdir_t;
10、lfs目录结构
-
// littlefs directory type -
typedef struct lfs_dir { -
struct lfs_dir *next; // 指向子目录 -
uint16_t id; -
uint8_t type; // LFS_TYPE_DIR -
lfs_mdir_t m; // 代表dir的metadata pair -
lfs_off_t pos; // 在目录中的当前位置,主要用在seek,tell和rewind操作中 -
lfs_block_t head[2]; -
} lfs_dir_t;
11、lfs文件类型
-
// littlefs file type -
typedef struct lfs_file { -
struct lfs_file *next; -
uint16_t id; // metadata tag中的id,在文件open时获取 -
uint8_t type; // LFS_TYPE_REG 或者 LFS_TYPE_DIR -
lfs_mdir_t m; // 文件所在的目录的metadata pair -
struct lfs_ctz { -
lfs_block_t head; -
lfs_size_t size; -
} ctz; // 指向大文件的CTZ skip-list。对于小文件则直接inline了,无需CTZ skip-list -
uint32_t flags; // lfs_open_flags中的值 -
lfs_off_t pos; // 文件访问时的偏移 -
lfs_block_t block; // file当前的block -
lfs_off_t off; // 在block内的offset -
lfs_cache_t cache; // 文件访问时的cache -
const struct lfs_file_config *cfg; // 文件open时的配置参数,包含一个buffer以及用户属性 -
} lfs_file_t;
12、lfs superblock结构
-
typedef struct lfs_superblock { -
uint32_t version; // 文件系统的版本号 -
lfs_size_t block_size; // 文件系统的block size,和flash的block size不一定相同 -
lfs_size_t block_count; // 文件系统包含的block数量,每个block的大小等于上面的block size -
lfs_size_t name_max; // 文件名的最大长度 -
lfs_size_t file_max; // 文件的最大长度 -
lfs_size_t attr_max; // 用户属性的最大长度 -
} lfs_superblock_t;
13、lfs文件系统类型结构
-
// The littlefs filesystem type -
typedef struct lfs { -
lfs_cache_t rcache; // read cache -
lfs_cache_t pcache; // program cache -
lfs_block_t root[2]; // 根目录所在的block -
struct lfs_mlist { -
struct lfs_mlist *next; // 指向下一个节点 -
uint16_t id; // metadata pair的id -
uint8_t type; // metadata pair的类型 -
lfs_mdir_t m; // metadata pair -
} *mlist; // metadata pair list -
uint32_t seed; // block alloctor的随机数生成的seed -
struct lfs_gstate { -
uint32_t tag; -
lfs_block_t pair[2]; -
} gstate, gpending, gdelta; // 用于目录操作sync的global state, -
struct lfs_free { -
lfs_block_t off; // 记录lookahead buffer中起始block的偏移 -
lfs_block_t size; // lookahead buffer中block的数量,注意lookahead采用的是bitmap的形式,因此size=8*lookahead_size -
lfs_block_t i; // lookahead buffer内部的偏移地址 -
lfs_block_t ack; // 剩余block的数量,初始值为block count,如果该值为0,表示已经没有free block了 -
uint32_t *buffer; // buffer的长度为lookahead size -
} free; // lookahead buffer,用于分配free block -
const struct lfs_config *cfg; // 文件系统的配置参数 -
lfs_size_t name_max; // 文件名的最大长度,和superblock中的name_max值相同 -
lfs_size_t file_max; // 文件的最大长度,和superblock中的file_max值相同 -
lfs_size_t attr_max; // 用户属性的最大长度,和superblock中的attr_max值相同 -
} lfs_t;
----------------------------------------------------------------------------------------------------------------------------------------------------------------
小型文件系统FatFS和LittleFS对比和区别
对于许多物联网设备而言,拥有一个小型且具有弹性的文件系统至关重要。
在MCU上运行的文件系统不多,绝大部分人应该知道FatFS这个文件系统,今天就给大家讲讲FatFS和LittleFS的内容,以及他们之间的一些差异。
一、文件系统FatFS
FatFs是一个通用的文件系统(FAT/exFAT)模块,用于在小型嵌入式系统中实现FAT文件系统。
网址:
http://elm-chan.org/fsw/ff/00index_e.html
FatFs组件的编写遵循ANSI C(C89),完全分离于磁盘 I/O 层,因此不依赖于硬件平台。它可以嵌入到资源有限的微控制器中,如 8051, PIC, AVR, ARM, Z80, RX等等,不需要做任何修改。
---来自百度百科
特征
a.DOS/ Windows兼容的FAT/exFAT文件系统。
b.平台无关,容易移植。
c.程序代码和工作区的占用空间非常小。
d.支持以下各种配置选项:
-
ANSI / OEM或Unicode中的长文件名。
-
exFAT文件系统,64位LBA和GPT可存储大量数据。
-
RTOS的线程安全。
-
多个卷(物理驱动器和分区)。
-
可变扇区大小。
-
多个代码页,包括DBCS。
-
只读,可选API,I / O缓冲区等...


如果你会使用STM32CubeMX,想要使用FatFS非常容易,轻松几步就能将STM32“变成”一个U盘。
二、文件系统Littlefs
知道Littlefs文件系统的人相对比较少,但是如果使用过Mbed OS系统的人绝大部分都应该知道。
Mbed OS是Arm公司针对Cortex-M系列处理器,面向IoT开发的一套免费、开源开源嵌入式操作系统,专门为物联网中的“things”而设计。

而Littlefs只是Mbed其中的一部分内容,如下框图:

源码地址:
https://github.com/armmbed/mbed-littlefs
Littlefs特点:
-
占用资源小:物联网设备受到ROM和RAM的限制。
-
断电恢复能力:要求文件系统保持一致,并将数据刷新到底层存储。
-
平均磨损:通常情况下,存储支持每块数量有限的擦除,因此使用整个存储设备对于可靠性非常重要。

用法也挺简单,参看官方例程:
-
-
-
-
-
// Physical block device, can be any device that supports the BlockDevice API
-
SPIFBlockDevice bd(PTE2, PTE4, PTE1, PTE5);
-
-
-
// Storage for the littlefs
-
LittleFileSystem2 fs("fs");
-
-
-
// Entry point
-
int main() {
-
// Mount the filesystem
-
int err = fs.mount(&bd);
-
if (err) {
-
// Reformat if we can't mount the filesystem,
-
// this should only happen on the first boot
-
LittleFileSystem2::format(&bd);
-
fs.mount(&bd);
-
}
-
-
-
// Read the boot count
-
uint32_t boot_count = 0;
-
FILE *f = fopen( "/fs/boot_count", "r+");
-
if (!f) {
-
// Create the file if it doesn't exist
-
f = fopen( "/fs/boot_count", "w+");
-
}
-
fread(&boot_count, sizeof(boot_count), 1, f);
-
-
-
// Update the boot count
-
boot_count += 1;
-
rewind(f);
-
fwrite(&boot_count, sizeof(boot_count), 1, f);
-
-
-
// Remember that storage may not be updated until the file
-
// is closed successfully
-
fclose(f);
-
-
-
// Release any resources we were using
-
fs.unmount();
-
-
-
// Print the boot count
-
printf("boot_count: %ld\n", boot_count);
-
}
三、文件系统对比
每一种产物都有它存在的价值,文件系统也同样如此,各有各的优缺点,下面简单罗列几点它们的区别。
1.资源RAM / ROM大小
Littlefs是Mbed OS中的高完整性嵌入式文件系统,经过优化可与RAM和ROM有限的MCU一起使用。

Littlefs高度集成的嵌入式文件系统使用比FAT少的13K ROM和少于4K的RAM。
2.失电恢复能力
littlefs具有强大的copy-on-write保证,并且磁盘上的存储总是保持有效状态,可能有随机电源故障的系统适合该文件系统。
3.磨损均衡
嵌入式设备使用的大多数存储芯片都支持每个扇区有限的擦除集,如果没有均衡,则嵌入式设备的寿命可能会受到影响。
参考来源:
https://os.mbed.com/blog/entry/littlefs-high-integrity-embedded-fs/
