LittleFS轻量级文件系统:嵌入式Flash存储的最佳选择¶
学习目标¶
完成本教程后,你将能够:
- 理解LittleFS的设计原理和核心特性
- 掌握LittleFS的移植方法和配置
- 熟练使用LittleFS的API进行文件操作
- 理解LittleFS的掉电保护机制
- 掌握磨损均衡和垃圾回收原理
- 能够优化LittleFS的性能
- 完成一个完整的Flash文件系统项目
- 掌握LittleFS的调试和故障排除方法
前置要求¶
在开始学习之前,建议你具备:
知识要求: - 熟悉C语言编程 - 了解Flash存储器的基本特性 - 理解文件系统的基本概念 - 掌握SPI或I2C通信协议 - 了解RTOS的基本使用
技能要求: - 能够编写嵌入式C代码 - 会使用SPI Flash驱动 - 熟悉基本的文件操作 - 能够使用调试工具 - 了解内存管理
开发环境: - STM32或ESP32开发板 - SPI Flash芯片(如W25Q128) - Keil MDK或STM32CubeIDE - 串口调试工具 - 逻辑分析仪(可选)
LittleFS概述¶
什么是LittleFS¶
LittleFS是一个专为嵌入式系统设计的轻量级文件系统,特别适合用于Flash存储器。它由ARM公司开发并开源,具有以下显著特点:
核心特性:
- 掉电安全
- 所有操作都是原子性的
- 断电后文件系统保持一致性
- 不会丢失已写入的数据
-
支持事务性操作
-
磨损均衡
- 自动分散写入操作
- 延长Flash使用寿命
- 动态块分配
-
智能垃圾回收
-
资源占用小
- RAM占用极低(几KB)
- ROM占用小(约15KB)
- 适合资源受限的MCU
-
可配置的缓存大小
-
高可靠性
- 元数据冗余存储
- CRC校验保护
- 坏块管理
- 自动错误恢复
LittleFS架构¶
系统架构图:
┌─────────────────────────────────────────────────┐
│ 应用层 (Application) │
│ 文件读写、目录操作 │
└─────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────┐
│ LittleFS API层 │
│ lfs_file_open, lfs_read, lfs_write... │
└─────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────┐
│ LittleFS核心层 │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 文件管理 │ │ 目录管理 │ │
│ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 块分配器 │ │ 磨损均衡 │ │
│ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 元数据管理 │ │ 缓存管理 │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────┐
│ 块设备接口层 (Block Device) │
│ read, prog, erase, sync │
└─────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────┐
│ Flash驱动层 │
│ SPI Flash, QSPI Flash, 内部Flash │
└─────────────────────────────────────────────────┘
LittleFS vs 其他文件系统¶
| 特性 | LittleFS | FAT | SPIFFS |
|---|---|---|---|
| 掉电安全 | ✅ 完全支持 | ❌ 不支持 | ⚠️ 部分支持 |
| 磨损均衡 | ✅ 自动 | ❌ 无 | ✅ 支持 |
| RAM占用 | 极低(几KB) | 中等 | 低 |
| ROM占用 | 小(15KB) | 大 | 小 |
| 目录支持 | ✅ 完整支持 | ✅ 支持 | ❌ 不支持 |
| 文件属性 | ✅ 支持 | ✅ 支持 | ⚠️ 有限 |
| 性能 | 中等 | 高 | 中等 |
| 适用场景 | 嵌入式Flash | SD卡、U盘 | 小型Flash |
选择建议: - 需要掉电保护 → LittleFS - 需要与PC交互 → FAT - 资源极度受限 → SPIFFS - 大容量存储 → FAT32/exFAT
准备工作¶
硬件准备¶
| 名称 | 数量 | 说明 | 参考型号 |
|---|---|---|---|
| 开发板 | 1 | STM32F4或ESP32 | STM32F407VG |
| SPI Flash | 1 | 外部Flash芯片 | W25Q128(16MB) |
| 杜邦线 | 若干 | 连接Flash | - |
| USB线 | 1 | 供电和调试 | - |
软件准备¶
必需软件: - STM32CubeIDE 或 Arduino IDE(ESP32) - LittleFS源码:https://github.com/littlefs-project/littlefs - SPI Flash驱动库 - 串口调试助手
可选工具: - 逻辑分析仪(调试SPI通信) - Flash编程器(备份恢复)
电路连接¶
SPI Flash连接示意图:
STM32F407 W25Q128
PA5 (SCK) ────────► CLK
PA6 (MISO) ◄──────── DO
PA7 (MOSI) ────────► DI
PA4 (CS) ────────► CS
3.3V ────────► VCC
GND ────────► GND
连接说明: - 确保Flash芯片供电为3.3V - CS片选信号可以使用任意GPIO - 建议在MISO线上加上拉电阻(10KΩ) - 确保所有信号线长度尽量短
步骤1:获取和配置LittleFS源码¶
1.1 下载LittleFS¶
# 克隆LittleFS仓库
git clone https://github.com/littlefs-project/littlefs.git
# 或下载最新release版本
wget https://github.com/littlefs-project/littlefs/archive/refs/tags/v2.5.1.tar.gz
需要的文件:
- lfs.c - 核心实现
- lfs.h - API头文件
- lfs_util.c - 工具函数
- lfs_util.h - 工具函数头文件
1.2 配置LittleFS¶
创建配置文件 lfs_config.h:
#ifndef LFS_CONFIG_H
#define LFS_CONFIG_H
// 系统包含
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
// 调试配置
#ifdef DEBUG
#define LFS_DEBUG 1
#define LFS_WARN 1
#define LFS_ERROR 1
#else
#define LFS_DEBUG 0
#define LFS_WARN 0
#define LFS_ERROR 1
#endif
// 内存分配配置
#define LFS_NO_MALLOC 0 // 使用动态内存分配
#define LFS_NO_ASSERT 0 // 启用断言
// 缓存配置
#define LFS_READ_SIZE 256 // 最小读取单位
#define LFS_PROG_SIZE 256 // 最小编程单位
#define LFS_BLOCK_SIZE 4096 // 擦除块大小
#define LFS_BLOCK_COUNT 4096 // 总块数(16MB)
#define LFS_CACHE_SIZE 256 // 缓存大小
#define LFS_LOOKAHEAD_SIZE 16 // 预读大小
// 块设备配置
#define LFS_BLOCK_CYCLES 500 // 磨损均衡周期
// 文件系统限制
#define LFS_NAME_MAX 255 // 最大文件名长度
#define LFS_FILE_MAX 2147483647 // 最大文件大小
#define LFS_ATTR_MAX 1022 // 最大属性大小
#endif // LFS_CONFIG_H
配置说明:
- LFS_READ_SIZE: 必须是Flash页大小的倍数
- LFS_PROG_SIZE: 必须是Flash编程单位的倍数
- LFS_BLOCK_SIZE: 必须等于Flash擦除块大小
- LFS_BLOCK_COUNT: Flash总大小 / 块大小
- LFS_CACHE_SIZE: 影响性能和RAM占用
1.3 添加到工程¶
STM32CubeIDE步骤:
- 将LittleFS源文件添加到工程
- 右键项目 → New → Folder → 创建
Middlewares/LittleFS - 复制
lfs.c,lfs.h,lfs_util.c,lfs_util.h -
复制
lfs_config.h到工程 -
配置包含路径
- 右键项目 → Properties → C/C++ Build → Settings
- MCU GCC Compiler → Include paths
-
添加
Middlewares/LittleFS -
编译测试
步骤2:实现块设备接口¶
LittleFS需要我们实现底层的Flash读写接口。
2.1 定义块设备结构¶
创建文件 lfs_port.h:
#ifndef LFS_PORT_H
#define LFS_PORT_H
#include "lfs.h"
#include "w25qxx.h" // SPI Flash驱动头文件
// 块设备配置
#define FLASH_BLOCK_SIZE 4096 // 4KB擦除块
#define FLASH_BLOCK_COUNT 4096 // 16MB总容量
#define FLASH_PAGE_SIZE 256 // 256字节页
// 块设备接口函数
int block_device_read(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
int block_device_prog(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
int block_device_erase(const struct lfs_config *c, lfs_block_t block);
int block_device_sync(const struct lfs_config *c);
// 初始化函数
int lfs_port_init(void);
#endif // LFS_PORT_H
2.2 实现块设备接口¶
创建文件 lfs_port.c:
#include "lfs_port.h"
#include "w25qxx.h"
#include <string.h>
/**
* @brief 读取Flash数据
* @param c: LittleFS配置
* @param block: 块号
* @param off: 块内偏移
* @param buffer: 数据缓冲区
* @param size: 读取大小
* @retval 0:成功, 负数:错误
*/
int block_device_read(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size)
{
// 计算Flash地址
uint32_t addr = block * c->block_size + off;
// 读取Flash数据
if (W25QXX_Read((uint8_t*)buffer, addr, size) != 0) {
return LFS_ERR_IO;
}
return LFS_ERR_OK;
}
/**
* @brief 编程(写入)Flash数据
* @param c: LittleFS配置
* @param block: 块号
* @param off: 块内偏移
* @param buffer: 数据缓冲区
* @param size: 写入大小
* @retval 0:成功, 负数:错误
*/
int block_device_prog(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size)
{
// 计算Flash地址
uint32_t addr = block * c->block_size + off;
// 写入Flash数据
if (W25QXX_Write((uint8_t*)buffer, addr, size) != 0) {
return LFS_ERR_IO;
}
return LFS_ERR_OK;
}
/**
* @brief 擦除Flash块
* @param c: LittleFS配置
* @param block: 块号
* @retval 0:成功, 负数:错误
*/
int block_device_erase(const struct lfs_config *c, lfs_block_t block)
{
// 计算Flash地址
uint32_t addr = block * c->block_size;
// 擦除4KB扇区
if (W25QXX_Erase_Sector(addr) != 0) {
return LFS_ERR_IO;
}
return LFS_ERR_OK;
}
/**
* @brief 同步Flash数据
* @param c: LittleFS配置
* @retval 0:成功, 负数:错误
*/
int block_device_sync(const struct lfs_config *c)
{
// W25QXX不需要显式同步
// 如果使用带缓存的Flash,这里需要刷新缓存
return LFS_ERR_OK;
}
/**
* @brief 初始化块设备
* @retval 0:成功, 负数:错误
*/
int lfs_port_init(void)
{
// 初始化SPI Flash
if (W25QXX_Init() != 0) {
return -1;
}
// 检测Flash芯片
uint16_t id = W25QXX_ReadID();
if (id == 0 || id == 0xFFFF) {
return -1;
}
return 0;
}
代码说明:
- block_device_read: 从Flash读取数据,支持任意偏移和大小
- block_device_prog: 向Flash写入数据,必须先擦除
- block_device_erase: 擦除整个块(4KB)
- block_device_sync: 确保数据写入Flash(某些Flash需要)
步骤3:初始化和挂载文件系统¶
3.1 配置LittleFS¶
创建文件 lfs_app.c:
#include "lfs.h"
#include "lfs_port.h"
#include <stdio.h>
// LittleFS对象
static lfs_t lfs;
static lfs_file_t file;
// 配置结构
static const struct lfs_config cfg = {
// 块设备操作
.read = block_device_read,
.prog = block_device_prog,
.erase = block_device_erase,
.sync = block_device_sync,
// 块设备配置
.read_size = 256,
.prog_size = 256,
.block_size = 4096,
.block_count = 4096,
.cache_size = 256,
.lookahead_size = 16,
.block_cycles = 500,
};
/**
* @brief 初始化文件系统
* @retval 0:成功, 负数:错误
*/
int lfs_app_init(void)
{
int err;
// 初始化块设备
if (lfs_port_init() != 0) {
printf("Failed to initialize block device\n");
return -1;
}
// 尝试挂载文件系统
err = lfs_mount(&lfs, &cfg);
// 如果挂载失败,格式化后重新挂载
if (err) {
printf("File system not found, formatting...\n");
err = lfs_format(&lfs, &cfg);
if (err) {
printf("Failed to format: %d\n", err);
return err;
}
err = lfs_mount(&lfs, &cfg);
if (err) {
printf("Failed to mount after format: %d\n", err);
return err;
}
printf("File system formatted and mounted\n");
} else {
printf("File system mounted successfully\n");
}
return 0;
}
/**
* @brief 卸载文件系统
* @retval 0:成功, 负数:错误
*/
int lfs_app_deinit(void)
{
return lfs_unmount(&lfs);
}
3.2 在main函数中初始化¶
#include "lfs_app.h"
int main(void)
{
// 系统初始化
HAL_Init();
SystemClock_Config();
// 初始化外设
MX_GPIO_Init();
MX_SPI1_Init();
MX_USART1_UART_Init();
printf("LittleFS Demo Starting...\n");
// 初始化文件系统
if (lfs_app_init() != 0) {
printf("Failed to initialize file system\n");
Error_Handler();
}
// 应用程序代码
// ...
while (1) {
// 主循环
}
}
预期输出:
或首次运行时:
步骤4:文件操作实战¶
4.1 创建和写入文件¶
/**
* @brief 写入文件示例
*/
void lfs_write_example(void)
{
int err;
const char *filename = "test.txt";
const char *content = "Hello, LittleFS!\n";
// 打开文件(创建或覆盖)
err = lfs_file_open(&lfs, &file, filename,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
if (err) {
printf("Failed to open file: %d\n", err);
return;
}
// 写入数据
lfs_ssize_t written = lfs_file_write(&lfs, &file, content, strlen(content));
if (written < 0) {
printf("Failed to write file: %d\n", (int)written);
lfs_file_close(&lfs, &file);
return;
}
printf("Written %d bytes to %s\n", (int)written, filename);
// 关闭文件
err = lfs_file_close(&lfs, &file);
if (err) {
printf("Failed to close file: %d\n", err);
}
}
文件打开标志:
- LFS_O_RDONLY: 只读模式
- LFS_O_WRONLY: 只写模式
- LFS_O_RDWR: 读写模式
- LFS_O_CREAT: 文件不存在时创建
- LFS_O_EXCL: 文件存在时失败
- LFS_O_TRUNC: 截断文件为0
- LFS_O_APPEND: 追加模式
4.2 读取文件¶
/**
* @brief 读取文件示例
*/
void lfs_read_example(void)
{
int err;
const char *filename = "test.txt";
char buffer[128];
// 打开文件(只读)
err = lfs_file_open(&lfs, &file, filename, LFS_O_RDONLY);
if (err) {
printf("Failed to open file: %d\n", err);
return;
}
// 读取数据
lfs_ssize_t read_size = lfs_file_read(&lfs, &file, buffer, sizeof(buffer) - 1);
if (read_size < 0) {
printf("Failed to read file: %d\n", (int)read_size);
lfs_file_close(&lfs, &file);
return;
}
// 添加字符串结束符
buffer[read_size] = '\0';
printf("Read %d bytes from %s:\n%s\n", (int)read_size, filename, buffer);
// 关闭文件
lfs_file_close(&lfs, &file);
}
4.3 追加写入¶
/**
* @brief 追加写入示例
*/
void lfs_append_example(void)
{
int err;
const char *filename = "log.txt";
char log_entry[64];
// 生成日志条目
snprintf(log_entry, sizeof(log_entry), "[%lu] System running\n", HAL_GetTick());
// 打开文件(追加模式)
err = lfs_file_open(&lfs, &file, filename,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND);
if (err) {
printf("Failed to open file: %d\n", err);
return;
}
// 写入日志
lfs_ssize_t written = lfs_file_write(&lfs, &file, log_entry, strlen(log_entry));
if (written < 0) {
printf("Failed to write log: %d\n", (int)written);
} else {
printf("Log written: %s", log_entry);
}
// 关闭文件
lfs_file_close(&lfs, &file);
}
4.4 文件定位¶
/**
* @brief 文件定位示例
*/
void lfs_seek_example(void)
{
int err;
const char *filename = "data.bin";
uint8_t data[16];
// 打开文件
err = lfs_file_open(&lfs, &file, filename, LFS_O_RDONLY);
if (err) {
printf("Failed to open file: %d\n", err);
return;
}
// 获取文件大小
lfs_soff_t size = lfs_file_size(&lfs, &file);
printf("File size: %d bytes\n", (int)size);
// 定位到文件中间
lfs_soff_t pos = lfs_file_seek(&lfs, &file, size / 2, LFS_SEEK_SET);
printf("Seeked to position: %d\n", (int)pos);
// 读取数据
lfs_ssize_t read_size = lfs_file_read(&lfs, &file, data, sizeof(data));
printf("Read %d bytes from middle of file\n", (int)read_size);
// 定位到文件开头
lfs_file_rewind(&lfs, &file);
printf("Rewound to beginning\n");
// 关闭文件
lfs_file_close(&lfs, &file);
}
定位模式:
- LFS_SEEK_SET: 从文件开头定位
- LFS_SEEK_CUR: 从当前位置定位
- LFS_SEEK_END: 从文件末尾定位
步骤5:目录操作¶
5.1 创建目录¶
/**
* @brief 创建目录示例
*/
void lfs_mkdir_example(void)
{
int err;
// 创建目录
err = lfs_mkdir(&lfs, "logs");
if (err && err != LFS_ERR_EXIST) {
printf("Failed to create directory: %d\n", err);
return;
}
printf("Directory 'logs' created\n");
// 创建嵌套目录
err = lfs_mkdir(&lfs, "data");
if (err && err != LFS_ERR_EXIST) {
printf("Failed to create directory: %d\n", err);
return;
}
err = lfs_mkdir(&lfs, "data/sensors");
if (err && err != LFS_ERR_EXIST) {
printf("Failed to create subdirectory: %d\n", err);
return;
}
printf("Nested directories created\n");
}
5.2 遍历目录¶
/**
* @brief 遍历目录示例
*/
void lfs_dir_list_example(void)
{
int err;
lfs_dir_t dir;
struct lfs_info info;
// 打开根目录
err = lfs_dir_open(&lfs, &dir, "/");
if (err) {
printf("Failed to open directory: %d\n", err);
return;
}
printf("Directory listing:\n");
printf("%-20s %10s %s\n", "Name", "Size", "Type");
printf("----------------------------------------\n");
// 遍历目录项
while (true) {
err = lfs_dir_read(&lfs, &dir, &info);
if (err < 0) {
printf("Failed to read directory: %d\n", err);
break;
}
// 读取完毕
if (err == 0) {
break;
}
// 跳过 . 和 ..
if (strcmp(info.name, ".") == 0 || strcmp(info.name, "..") == 0) {
continue;
}
// 打印文件信息
const char *type = (info.type == LFS_TYPE_DIR) ? "DIR" : "FILE";
printf("%-20s %10d %s\n", info.name, (int)info.size, type);
}
// 关闭目录
lfs_dir_close(&lfs, &dir);
}
5.3 删除文件和目录¶
/**
* @brief 删除文件和目录示例
*/
void lfs_remove_example(void)
{
int err;
// 删除文件
err = lfs_remove(&lfs, "test.txt");
if (err) {
printf("Failed to remove file: %d\n", err);
} else {
printf("File 'test.txt' removed\n");
}
// 删除空目录
err = lfs_remove(&lfs, "logs");
if (err) {
printf("Failed to remove directory: %d\n", err);
} else {
printf("Directory 'logs' removed\n");
}
}
5.4 重命名文件¶
/**
* @brief 重命名文件示例
*/
void lfs_rename_example(void)
{
int err;
// 重命名文件
err = lfs_rename(&lfs, "old_name.txt", "new_name.txt");
if (err) {
printf("Failed to rename file: %d\n", err);
} else {
printf("File renamed successfully\n");
}
// 移动文件到其他目录
err = lfs_rename(&lfs, "file.txt", "data/file.txt");
if (err) {
printf("Failed to move file: %d\n", err);
} else {
printf("File moved successfully\n");
}
}
步骤6:文件属性操作¶
6.1 获取文件信息¶
/**
* @brief 获取文件信息示例
*/
void lfs_stat_example(void)
{
int err;
struct lfs_info info;
// 获取文件信息
err = lfs_stat(&lfs, "test.txt", &info);
if (err) {
printf("Failed to stat file: %d\n", err);
return;
}
// 打印文件信息
printf("File information:\n");
printf(" Name: %s\n", info.name);
printf(" Type: %s\n", (info.type == LFS_TYPE_DIR) ? "Directory" : "File");
printf(" Size: %d bytes\n", (int)info.size);
}
6.2 设置和获取自定义属性¶
/**
* @brief 文件属性示例
*/
void lfs_attr_example(void)
{
int err;
const char *filename = "config.dat";
// 定义自定义属性
struct {
uint32_t version;
uint32_t crc;
} attr_data = {
.version = 0x0100,
.crc = 0x12345678
};
// 打开文件
err = lfs_file_open(&lfs, &file, filename,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
if (err) {
printf("Failed to open file: %d\n", err);
return;
}
// 设置自定义属性
err = lfs_setattr(&lfs, filename, 0x01, &attr_data, sizeof(attr_data));
if (err) {
printf("Failed to set attribute: %d\n", err);
} else {
printf("Attribute set successfully\n");
}
lfs_file_close(&lfs, &file);
// 读取自定义属性
struct {
uint32_t version;
uint32_t crc;
} read_attr;
lfs_ssize_t attr_size = lfs_getattr(&lfs, filename, 0x01,
&read_attr, sizeof(read_attr));
if (attr_size < 0) {
printf("Failed to get attribute: %d\n", (int)attr_size);
} else {
printf("Attribute read: version=0x%04X, crc=0x%08X\n",
(unsigned int)read_attr.version, (unsigned int)read_attr.crc);
}
}
步骤7:掉电保护测试¶
7.1 掉电保护原理¶
LittleFS的掉电保护基于以下机制:
元数据对(Metadata Pairs):
┌─────────────────┐
│ Metadata A │ ← 当前有效
│ - 版本: 5 │
│ - CRC: OK │
└─────────────────┘
┌─────────────────┐
│ Metadata B │ ← 备份
│ - 版本: 4 │
│ - CRC: OK │
└─────────────────┘
写入流程: 1. 写入新数据到备份块 2. 更新元数据版本号 3. 写入CRC校验 4. 原子性切换指针
恢复流程: 1. 检查两个元数据块的CRC 2. 选择版本号较新且CRC正确的块 3. 如果都损坏,使用最后一个好的备份
7.2 掉电保护测试代码¶
/**
* @brief 掉电保护测试
*/
void lfs_power_loss_test(void)
{
int err;
const char *filename = "critical.dat";
uint32_t counter = 0;
printf("Power loss protection test\n");
printf("Press button to simulate power loss\n");
while (1) {
// 打开文件
err = lfs_file_open(&lfs, &file, filename,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
if (err) {
printf("Failed to open file: %d\n", err);
break;
}
// 写入计数器
lfs_file_write(&lfs, &file, &counter, sizeof(counter));
// 同步数据(确保写入Flash)
lfs_file_sync(&lfs, &file);
// 关闭文件
lfs_file_close(&lfs, &file);
printf("Written counter: %lu\n", counter);
counter++;
// 延时
HAL_Delay(1000);
// 检查按键(模拟掉电)
if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_RESET) {
printf("Simulating power loss...\n");
// 不卸载文件系统,直接重启
NVIC_SystemReset();
}
}
}
/**
* @brief 验证掉电后的数据
*/
void lfs_verify_after_power_loss(void)
{
int err;
const char *filename = "critical.dat";
uint32_t counter;
// 打开文件
err = lfs_file_open(&lfs, &file, filename, LFS_O_RDONLY);
if (err) {
printf("File not found (expected on first run)\n");
return;
}
// 读取计数器
lfs_ssize_t read_size = lfs_file_read(&lfs, &file, &counter, sizeof(counter));
if (read_size == sizeof(counter)) {
printf("Recovered counter after power loss: %lu\n", counter);
printf("Data integrity verified!\n");
} else {
printf("Failed to read counter\n");
}
lfs_file_close(&lfs, &file);
}
测试步骤: 1. 运行掉电保护测试程序 2. 等待写入几次数据 3. 按下按键模拟掉电(系统重启) 4. 系统重启后验证数据完整性 5. 应该能读取到最后成功写入的计数器值
步骤8:性能优化¶
8.1 缓存优化¶
/**
* @brief 优化的配置(增大缓存)
*/
static const struct lfs_config cfg_optimized = {
.read = block_device_read,
.prog = block_device_prog,
.erase = block_device_erase,
.sync = block_device_sync,
// 增大缓存提升性能
.read_size = 256,
.prog_size = 256,
.block_size = 4096,
.block_count = 4096,
.cache_size = 512, // 增大到512字节
.lookahead_size = 32, // 增大预读
.block_cycles = 500,
};
缓存大小影响: - 更大的缓存 → 更好的性能,更多RAM占用 - 建议:cache_size = block_size / 8 到 block_size / 4 - 最小值:必须 ≥ read_size 和 prog_size
8.2 批量写入优化¶
/**
* @brief 批量写入优化示例
*/
void lfs_batch_write_example(void)
{
int err;
const char *filename = "data.bin";
uint8_t buffer[1024];
// 打开文件
err = lfs_file_open(&lfs, &file, filename,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
if (err) {
printf("Failed to open file: %d\n", err);
return;
}
// 批量写入(减少sync次数)
for (int i = 0; i < 100; i++) {
// 准备数据
memset(buffer, i, sizeof(buffer));
// 写入数据
lfs_file_write(&lfs, &file, buffer, sizeof(buffer));
// 每10次同步一次(而不是每次都同步)
if ((i + 1) % 10 == 0) {
lfs_file_sync(&lfs, &file);
}
}
// 最后同步
lfs_file_sync(&lfs, &file);
// 关闭文件
lfs_file_close(&lfs, &file);
printf("Batch write completed\n");
}
8.3 预分配空间¶
/**
* @brief 预分配文件空间
*/
void lfs_preallocate_example(void)
{
int err;
const char *filename = "large_file.dat";
// 打开文件
err = lfs_file_open(&lfs, &file, filename,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
if (err) {
printf("Failed to open file: %d\n", err);
return;
}
// 预分配10MB空间
err = lfs_file_truncate(&lfs, &file, 10 * 1024 * 1024);
if (err) {
printf("Failed to preallocate space: %d\n", err);
lfs_file_close(&lfs, &file);
return;
}
printf("Space preallocated\n");
// 后续写入会更快
// ...
lfs_file_close(&lfs, &file);
}
8.4 性能测试¶
/**
* @brief 性能测试
*/
void lfs_performance_test(void)
{
int err;
const char *filename = "perf_test.dat";
uint8_t buffer[1024];
uint32_t start_tick, end_tick;
printf("Performance Test\n");
// 写入测试
start_tick = HAL_GetTick();
err = lfs_file_open(&lfs, &file, filename,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
if (err) {
printf("Failed to open file: %d\n", err);
return;
}
// 写入1MB数据
for (int i = 0; i < 1024; i++) {
lfs_file_write(&lfs, &file, buffer, sizeof(buffer));
}
lfs_file_close(&lfs, &file);
end_tick = HAL_GetTick();
uint32_t write_time = end_tick - start_tick;
float write_speed = 1024.0 / (write_time / 1000.0); // KB/s
printf("Write: 1MB in %lu ms (%.2f KB/s)\n", write_time, write_speed);
// 读取测试
start_tick = HAL_GetTick();
err = lfs_file_open(&lfs, &file, filename, LFS_O_RDONLY);
if (err) {
printf("Failed to open file: %d\n", err);
return;
}
// 读取1MB数据
for (int i = 0; i < 1024; i++) {
lfs_file_read(&lfs, &file, buffer, sizeof(buffer));
}
lfs_file_close(&lfs, &file);
end_tick = HAL_GetTick();
uint32_t read_time = end_tick - start_tick;
float read_speed = 1024.0 / (read_time / 1000.0); // KB/s
printf("Read: 1MB in %lu ms (%.2f KB/s)\n", read_time, read_speed);
}
步骤9:调试和故障排除¶
9.1 启用调试输出¶
在 lfs_config.h 中启用调试:
// 调试配置
#define LFS_DEBUG 1
#define LFS_WARN 1
#define LFS_ERROR 1
// 自定义调试输出
#define LFS_DEBUG_PRINTF(fmt, ...) printf("[LFS DEBUG] " fmt, ##__VA_ARGS__)
#define LFS_WARN_PRINTF(fmt, ...) printf("[LFS WARN] " fmt, ##__VA_ARGS__)
#define LFS_ERROR_PRINTF(fmt, ...) printf("[LFS ERROR] " fmt, ##__VA_ARGS__)
9.2 文件系统检查¶
/**
* @brief 文件系统完整性检查
*/
void lfs_fsck(void)
{
int err;
printf("Checking file system integrity...\n");
// LittleFS会在挂载时自动检查
// 如果有问题会返回错误
err = lfs_unmount(&lfs);
if (err) {
printf("Unmount failed: %d\n", err);
}
err = lfs_mount(&lfs, &cfg);
if (err) {
printf("File system corrupted: %d\n", err);
printf("Attempting to format...\n");
err = lfs_format(&lfs, &cfg);
if (err) {
printf("Format failed: %d\n", err);
} else {
printf("File system formatted successfully\n");
lfs_mount(&lfs, &cfg);
}
} else {
printf("File system is healthy\n");
}
}
9.3 获取文件系统信息¶
/**
* @brief 获取文件系统统计信息
*/
void lfs_get_info(void)
{
lfs_ssize_t used_blocks = lfs_fs_size(&lfs);
if (used_blocks < 0) {
printf("Failed to get fs size: %d\n", (int)used_blocks);
return;
}
uint32_t total_size = cfg.block_count * cfg.block_size;
uint32_t used_size = used_blocks * cfg.block_size;
uint32_t free_size = total_size - used_size;
printf("File System Information:\n");
printf(" Total size: %lu KB\n", total_size / 1024);
printf(" Used size: %lu KB\n", used_size / 1024);
printf(" Free size: %lu KB\n", free_size / 1024);
printf(" Usage: %lu%%\n", (used_size * 100) / total_size);
}
9.4 常见错误处理¶
/**
* @brief 错误码转字符串
*/
const char* lfs_error_to_string(int err)
{
switch (err) {
case LFS_ERR_OK: return "No error";
case LFS_ERR_IO: return "I/O error";
case LFS_ERR_CORRUPT: return "Corrupted";
case LFS_ERR_NOENT: return "No entry";
case LFS_ERR_EXIST: return "Entry exists";
case LFS_ERR_NOTDIR: return "Not a directory";
case LFS_ERR_ISDIR: return "Is a directory";
case LFS_ERR_NOTEMPTY: return "Directory not empty";
case LFS_ERR_BADF: return "Bad file descriptor";
case LFS_ERR_FBIG: return "File too large";
case LFS_ERR_INVAL: return "Invalid parameter";
case LFS_ERR_NOSPC: return "No space left";
case LFS_ERR_NOMEM: return "No memory";
case LFS_ERR_NOATTR: return "No attribute";
case LFS_ERR_NAMETOOLONG: return "Name too long";
default: return "Unknown error";
}
}
/**
* @brief 安全的文件操作包装
*/
int safe_file_write(const char *filename, const void *data, size_t size)
{
int err;
err = lfs_file_open(&lfs, &file, filename,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
if (err) {
printf("Failed to open %s: %s\n", filename, lfs_error_to_string(err));
return err;
}
lfs_ssize_t written = lfs_file_write(&lfs, &file, data, size);
if (written < 0) {
printf("Failed to write %s: %s\n", filename, lfs_error_to_string((int)written));
lfs_file_close(&lfs, &file);
return (int)written;
}
if ((size_t)written != size) {
printf("Partial write to %s: %d/%d bytes\n", filename, (int)written, (int)size);
}
err = lfs_file_close(&lfs, &file);
if (err) {
printf("Failed to close %s: %s\n", filename, lfs_error_to_string(err));
return err;
}
return LFS_ERR_OK;
}
完整示例项目¶
项目:数据记录器¶
实现一个完整的数据记录系统,包含配置管理、数据记录和查询功能。
/**
* @file data_logger.c
* @brief 基于LittleFS的数据记录器
*/
#include "lfs.h"
#include "lfs_app.h"
#include <stdio.h>
#include <time.h>
#define LOG_DIR "logs"
#define CONFIG_FILE "config.dat"
#define MAX_LOG_SIZE (100 * 1024) // 100KB
/**
* 配置结构
*/
typedef struct {
uint32_t magic; // 魔数
uint32_t version; // 版本号
uint32_t log_interval; // 记录间隔(秒)
uint32_t max_log_files; // 最大日志文件数
uint8_t enable_compress; // 是否压缩
} config_t;
/**
* 数据记录结构
*/
typedef struct {
uint32_t timestamp; // 时间戳
float temperature; // 温度
float humidity; // 湿度
uint16_t pressure; // 气压
uint8_t battery; // 电池电量
} log_entry_t;
static config_t g_config;
static lfs_file_t g_log_file;
static char g_current_log[64];
/**
* @brief 加载配置
*/
int load_config(void)
{
int err;
lfs_file_t file;
err = lfs_file_open(&lfs, &file, CONFIG_FILE, LFS_O_RDONLY);
if (err) {
// 配置文件不存在,使用默认配置
printf("Config not found, using defaults\n");
g_config.magic = 0x12345678;
g_config.version = 1;
g_config.log_interval = 60;
g_config.max_log_files = 10;
g_config.enable_compress = 0;
return 0;
}
lfs_ssize_t read_size = lfs_file_read(&lfs, &file, &g_config, sizeof(g_config));
lfs_file_close(&lfs, &file);
if (read_size != sizeof(g_config) || g_config.magic != 0x12345678) {
printf("Invalid config, using defaults\n");
return -1;
}
printf("Config loaded: interval=%lu, max_files=%lu\n",
g_config.log_interval, g_config.max_log_files);
return 0;
}
/**
* @brief 保存配置
*/
int save_config(void)
{
int err;
lfs_file_t file;
err = lfs_file_open(&lfs, &file, CONFIG_FILE,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
if (err) {
printf("Failed to open config file: %d\n", err);
return err;
}
lfs_file_write(&lfs, &file, &g_config, sizeof(g_config));
lfs_file_sync(&lfs, &file);
lfs_file_close(&lfs, &file);
printf("Config saved\n");
return 0;
}
/**
* @brief 创建新的日志文件
*/
int create_new_log_file(void)
{
int err;
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
// 确保日志目录存在
err = lfs_mkdir(&lfs, LOG_DIR);
if (err && err != LFS_ERR_EXIST) {
printf("Failed to create log directory: %d\n", err);
return err;
}
// 生成日志文件名
snprintf(g_current_log, sizeof(g_current_log),
"%s/%04d%02d%02d_%02d%02d%02d.log",
LOG_DIR,
tm_info->tm_year + 1900,
tm_info->tm_mon + 1,
tm_info->tm_mday,
tm_info->tm_hour,
tm_info->tm_min,
tm_info->tm_sec);
// 打开新日志文件
err = lfs_file_open(&lfs, &g_log_file, g_current_log,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND);
if (err) {
printf("Failed to create log file: %d\n", err);
return err;
}
printf("Created log file: %s\n", g_current_log);
return 0;
}
/**
* @brief 写入日志条目
*/
int write_log_entry(const log_entry_t *entry)
{
lfs_ssize_t written;
// 检查文件大小
lfs_soff_t size = lfs_file_size(&lfs, &g_log_file);
if (size >= MAX_LOG_SIZE) {
// 关闭当前文件,创建新文件
lfs_file_close(&lfs, &g_log_file);
if (create_new_log_file() != 0) {
return -1;
}
}
// 写入日志条目
written = lfs_file_write(&lfs, &g_log_file, entry, sizeof(log_entry_t));
if (written != sizeof(log_entry_t)) {
printf("Failed to write log entry\n");
return -1;
}
// 定期同步
static int sync_counter = 0;
if (++sync_counter >= 10) {
lfs_file_sync(&lfs, &g_log_file);
sync_counter = 0;
}
return 0;
}
/**
* @brief 读取日志文件
*/
int read_log_file(const char *filename)
{
int err;
lfs_file_t file;
log_entry_t entry;
int count = 0;
err = lfs_file_open(&lfs, &file, filename, LFS_O_RDONLY);
if (err) {
printf("Failed to open log file: %d\n", err);
return err;
}
printf("Reading log file: %s\n", filename);
printf("%-12s %-8s %-8s %-8s %-8s\n",
"Timestamp", "Temp", "Humid", "Press", "Battery");
printf("--------------------------------------------------------\n");
while (1) {
lfs_ssize_t read_size = lfs_file_read(&lfs, &file, &entry, sizeof(entry));
if (read_size == 0) {
break; // EOF
}
if (read_size != sizeof(entry)) {
printf("Corrupted entry at position %d\n", count);
break;
}
printf("%-12lu %-8.2f %-8.2f %-8u %-8u\n",
entry.timestamp,
entry.temperature,
entry.humidity,
entry.pressure,
entry.battery);
count++;
}
lfs_file_close(&lfs, &file);
printf("Total entries: %d\n", count);
return 0;
}
/**
* @brief 列出所有日志文件
*/
int list_log_files(void)
{
int err;
lfs_dir_t dir;
struct lfs_info info;
err = lfs_dir_open(&lfs, &dir, LOG_DIR);
if (err) {
printf("Failed to open log directory: %d\n", err);
return err;
}
printf("Log files:\n");
printf("%-30s %10s\n", "Filename", "Size");
printf("----------------------------------------\n");
while (true) {
err = lfs_dir_read(&lfs, &dir, &info);
if (err < 0) {
break;
}
if (err == 0) {
break;
}
if (info.type == LFS_TYPE_REG) {
printf("%-30s %10d\n", info.name, (int)info.size);
}
}
lfs_dir_close(&lfs, &dir);
return 0;
}
/**
* @brief 清理旧日志文件
*/
int cleanup_old_logs(void)
{
// 实现日志文件清理逻辑
// 保留最新的N个文件,删除旧文件
printf("Cleaning up old log files...\n");
// TODO: 实现清理逻辑
return 0;
}
/**
* @brief 主函数示例
*/
void data_logger_demo(void)
{
log_entry_t entry;
// 初始化文件系统
if (lfs_app_init() != 0) {
printf("Failed to initialize file system\n");
return;
}
// 加载配置
load_config();
// 创建日志文件
if (create_new_log_file() != 0) {
printf("Failed to create log file\n");
return;
}
// 模拟数据记录
for (int i = 0; i < 100; i++) {
// 读取传感器数据(这里使用模拟数据)
entry.timestamp = HAL_GetTick() / 1000;
entry.temperature = 25.0f + (rand() % 100) / 10.0f;
entry.humidity = 60.0f + (rand() % 200) / 10.0f;
entry.pressure = 1013 + (rand() % 20);
entry.battery = 80 + (rand() % 20);
// 写入日志
if (write_log_entry(&entry) != 0) {
printf("Failed to write log entry\n");
break;
}
printf("Logged: T=%.1f H=%.1f P=%u B=%u\n",
entry.temperature, entry.humidity,
entry.pressure, entry.battery);
// 延时
HAL_Delay(g_config.log_interval * 1000);
}
// 关闭日志文件
lfs_file_close(&lfs, &g_log_file);
// 列出所有日志文件
list_log_files();
// 读取最新的日志文件
read_log_file(g_current_log);
// 清理旧日志
cleanup_old_logs();
// 卸载文件系统
lfs_app_deinit();
}
故障排除¶
问题1:挂载失败¶
现象:
可能原因: - Flash未正确初始化 - Flash芯片损坏 - 配置参数错误 - 文件系统损坏
解决方法: 1. 检查Flash连接和初始化
-
验证配置参数
-
重新格式化
问题2:写入失败¶
现象:
可能原因: - Flash空间不足 - 磨损均衡导致可用空间减少 - 文件碎片过多
解决方法: 1. 检查可用空间
-
删除不需要的文件
-
重新格式化(会丢失所有数据)
问题3:性能慢¶
现象: - 读写速度很慢 - 操作延迟高
可能原因: - 缓存太小 - 频繁同步 - Flash性能差
解决方法: 1. 增大缓存
-
减少同步频率
-
使用更快的Flash芯片
问题4:掉电后数据丢失¶
现象: - 掉电后文件损坏 - 最后写入的数据丢失
可能原因: - 未正确同步 - 掉电时机不当 - Flash写入未完成
解决方法: 1. 确保及时同步
-
使用事务性写入
-
添加电源监控
总结¶
通过本教程,你学习了:
- ✅ LittleFS的设计原理和核心特性
- ✅ 如何移植LittleFS到嵌入式系统
- ✅ 实现块设备接口的方法
- ✅ 文件和目录操作的完整API
- ✅ 掉电保护机制的原理和测试
- ✅ 性能优化的技巧和方法
- ✅ 调试和故障排除的方法
- ✅ 完整的数据记录器项目实现
关键要点:
- 掉电安全
- 所有操作都是原子性的
- 元数据冗余存储
-
及时同步确保数据安全
-
资源占用
- RAM占用极低(几KB)
- 适合资源受限的MCU
-
可配置的缓存大小
-
性能优化
- 增大缓存提升性能
- 批量操作减少同步
-
预分配空间提高效率
-
可靠性
- CRC校验保护
- 自动错误恢复
- 磨损均衡延长寿命
进阶挑战¶
尝试以下挑战来巩固学习:
- 挑战1:实现日志轮转
- 自动创建新日志文件
- 保留最新N个文件
-
自动删除旧文件
-
挑战2:添加数据压缩
- 集成压缩算法(如LZ4)
- 压缩日志数据
-
节省存储空间
-
挑战3:实现文件加密
- 使用AES加密文件
- 保护敏感数据
-
密钥管理
-
挑战4:多线程支持
- 添加互斥锁保护
- 支持多任务访问
-
避免竞态条件
-
挑战5:实现文件系统备份
- 备份到SD卡
- 恢复文件系统
- 数据迁移
下一步¶
建议继续学习:
- SPIFFS文件系统 - 另一种轻量级文件系统
- 文件系统移植与集成 - 深入学习移植技巧
- Flash文件系统设计 - 文件系统设计原理
- SD卡存储应用 - 大容量存储方案
参考资料¶
官方资源: 1. LittleFS GitHub - 官方仓库 2. LittleFS设计文档 - 设计原理 3. LittleFS规范 - 技术规范
技术文章: 1. ARM Mbed OS文档 - LittleFS使用指南 2. ESP-IDF文档 - LittleFS集成 3. Zephyr RTOS文档 - 文件系统支持
相关标准: 1. POSIX文件系统接口 2. Flash存储器规范 3. 嵌入式文件系统最佳实践
开发工具: 1. littlefs-fuse - FUSE挂载工具 2. mklfs - 镜像创建工具 3. Flash编程工具 - 备份和恢复
社区资源: 1. LittleFS讨论区 2. 嵌入式开发论坛 3. Stack Overflow相关问题
附录A:完整API参考¶
文件系统操作¶
// 格式化文件系统
int lfs_format(lfs_t *lfs, const struct lfs_config *config);
// 挂载文件系统
int lfs_mount(lfs_t *lfs, const struct lfs_config *config);
// 卸载文件系统
int lfs_unmount(lfs_t *lfs);
// 获取文件系统大小(已使用的块数)
lfs_ssize_t lfs_fs_size(lfs_t *lfs);
// 遍历文件系统(用于垃圾回收)
int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
文件操作¶
// 打开文件
int lfs_file_open(lfs_t *lfs, lfs_file_t *file, const char *path, int flags);
// 关闭文件
int lfs_file_close(lfs_t *lfs, lfs_file_t *file);
// 同步文件
int lfs_file_sync(lfs_t *lfs, lfs_file_t *file);
// 读取文件
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, void *buffer, lfs_size_t size);
// 写入文件
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, const void *buffer, lfs_size_t size);
// 定位文件
lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, lfs_soff_t off, int whence);
// 截断文件
int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size);
// 获取文件位置
lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file);
// 回到文件开头
int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);
// 获取文件大小
lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);
目录操作¶
// 创建目录
int lfs_mkdir(lfs_t *lfs, const char *path);
// 打开目录
int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path);
// 关闭目录
int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir);
// 读取目录项
int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info);
// 定位目录
int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off);
// 获取目录位置
lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir);
// 回到目录开头
int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
文件系统操作¶
// 删除文件或目录
int lfs_remove(lfs_t *lfs, const char *path);
// 重命名文件或目录
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);
// 获取文件信息
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
// 设置属性
int lfs_setattr(lfs_t *lfs, const char *path, uint8_t type, const void *buffer, lfs_size_t size);
// 获取属性
lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, uint8_t type, void *buffer, lfs_size_t size);
// 删除属性
int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type);
附录B:错误码参考¶
| 错误码 | 值 | 说明 |
|---|---|---|
| LFS_ERR_OK | 0 | 成功 |
| LFS_ERR_IO | -5 | I/O错误 |
| LFS_ERR_CORRUPT | -84 | 文件系统损坏 |
| LFS_ERR_NOENT | -2 | 文件或目录不存在 |
| LFS_ERR_EXIST | -17 | 文件或目录已存在 |
| LFS_ERR_NOTDIR | -20 | 不是目录 |
| LFS_ERR_ISDIR | -21 | 是目录 |
| LFS_ERR_NOTEMPTY | -39 | 目录非空 |
| LFS_ERR_BADF | -9 | 无效的文件描述符 |
| LFS_ERR_FBIG | -27 | 文件太大 |
| LFS_ERR_INVAL | -22 | 无效参数 |
| LFS_ERR_NOSPC | -28 | 空间不足 |
| LFS_ERR_NOMEM | -12 | 内存不足 |
| LFS_ERR_NOATTR | -61 | 属性不存在 |
| LFS_ERR_NAMETOOLONG | -36 | 文件名太长 |
附录C:配置参数说明¶
| 参数 | 类型 | 说明 | 推荐值 |
|---|---|---|---|
| read_size | uint32_t | 最小读取单位 | 256 |
| prog_size | uint32_t | 最小编程单位 | 256 |
| block_size | uint32_t | 擦除块大小 | 4096 |
| block_count | uint32_t | 总块数 | 根据Flash容量 |
| cache_size | uint32_t | 缓存大小 | 256-512 |
| lookahead_size | uint32_t | 预读大小 | 16-32 |
| block_cycles | int32_t | 磨损均衡周期 | 500 |
| name_max | uint32_t | 最大文件名长度 | 255 |
| file_max | uint32_t | 最大文件大小 | 2147483647 |
| attr_max | uint32_t | 最大属性大小 | 1022 |
反馈:如果你在学习过程中遇到问题,欢迎在评论区留言或提交Issue!
贡献:发现错误或有改进建议?欢迎提交Pull Request!
许可:本教程采用 CC BY-SA 4.0 许可协议。