SGL 文件系统使用说明¶
SGL 内置了三种文件系统实现,通过统一的 VFS(虚拟文件系统)层提供 POSIX 风格的文件操作接口。本文以 附件 为示例,分三部分说明 FATFS、LittleFS 和 RAMFS 的使用方法。
目录¶
1. FATFS — 兼容 SD 卡 / 硬盘的 FAT 文件系统¶
FATFS 是 SGL 自带的 FAT12/16/32 文件系统实现,兼容 Windows/macOS/Linux 格式化的 SD 卡和硬盘,支持长文件名(LFN)。
1.1 底层接口对接¶
FATFS 通过 sgl_block_dev_t 结构体与底层存储设备交互。用户需要实现以下接口:
// 定义在 sgl/include/sgl_block.h
typedef struct sgl_block_dev {
int (*init)(struct sgl_block_dev *dev); // 设备初始化(可选,可设为 NULL)
int (*read_sectors)(struct sgl_block_dev *dev, uint32_t sector, uint8_t *buf, uint32_t count); // 读扇区
int (*write_sectors)(struct sgl_block_dev *dev, uint32_t sector, const uint8_t *buf, uint32_t count); // 写扇区
int (*ioctl)(struct sgl_block_dev *dev, uint8_t cmd, void *param); // 控制命令(可选)
int (*status)(struct sgl_block_dev *dev); // 状态查询(可选)
const sgl_block_dev_info_t *info; // 设备信息
} sgl_block_dev_t;
sgl_block_dev_info_t 结构体:
typedef struct sgl_block_dev_info {
uint32_t sector_count; // 总扇区数
uint32_t sector_size; // 每扇区字节数(通常 512)
uint32_t block_size; // 擦除块大小(以扇区为单位)
uint32_t erase_block_size; // 擦除块大小(以字节为单位)
uint32_t page_size; // 编程页大小(以字节为单位)
} sgl_block_dev_info_t;
1.2 用户需要实现的接口(以 SD 卡为例)¶
参考 附件 中的 SD 卡对接示例:
#include <sgl_fs.h>
/* 1. 设备初始化函数 */
int sdcard_init(struct sgl_block_dev *dev)
{
return (sdio_sd_init() == SD_OK) ? 0 : -1;
}
/* 2. 读扇区函数 */
int sdio_sd_read_sectors(struct sgl_block_dev *dev, uint32_t sector, uint8_t *buf, uint32_t count)
{
return (sd_read_disk(buf, sector, count) == SD_OK) ? 0 : -1;
}
/* 3. 写扇区函数 */
int sdio_sd_write_sectors(struct sgl_block_dev *dev, uint32_t sector, const uint8_t *buf, uint32_t count)
{
return (sd_write_disk((uint8_t *)buf, sector, count) == SD_OK) ? 0 : -1;
}
/* 4. 设备信息结构体 */
const static sgl_block_dev_info_t sdcard_info = {
.sector_size = 512, // 每扇区 512 字节
.block_size = 0, // 无擦除块限制
.erase_block_size = 0,
.page_size = 512,
};
/* 5. 块设备结构体实例 */
const sgl_block_dev_t sdcard_block_dev = {
.init = sdcard_init,
.read_sectors = sdio_sd_read_sectors,
.write_sectors = sdio_sd_write_sectors,
.info = &sdcard_info,
};
1.3 注册与挂载¶
#include "../sgl/fs/fatfs/sgl_fatfs.h"
/* 1. 注册 FATFS 文件系统类型(在 main 初始化时调用一次) */
sgl_fatfs_register();
/* 2. 挂载 FATFS 到挂载点 */
sgl_fs_mount("/", "fatfs", &sdcard_block_dev, NULL);
挂载配置参数(可选):
typedef struct {
uint8_t vol_id; // 卷 ID(0-9),默认 0
uint8_t opt; // 挂载选项(0=自动,1=强制挂载)
} sgl_fatfs_config_t;
// 示例:指定卷 ID
sgl_fatfs_config_t fatfs_cfg = {
.vol_id = 0,
.opt = 1,
};
sgl_fs_mount("/", "fatfs", &sdcard_block_dev, &fatfs_cfg);
1.4 文件读写示例¶
/* 写文件 */
int fd = sgl_fs_open("/test.txt", SGL_O_WRONLY | SGL_O_CREAT | SGL_O_TRUNC);
if (fd >= 0) {
const char *data = "Hello, SGL FATFS!";
sgl_fs_write(fd, data, strlen(data));
sgl_fs_close(fd);
}
/* 读文件 */
fd = sgl_fs_open("/test.txt", SGL_O_RDONLY);
if (fd >= 0) {
char buf[128] = {0};
sgl_fs_read(fd, buf, sizeof(buf) - 1);
printf("Read: %s\n", buf);
sgl_fs_close(fd);
}
1.5 目录操作示例¶
/* 创建目录 */
sgl_fs_mkdir("/mydir");
/* 遍历目录 */
int dd;
if (sgl_fs_opendir("/", &dd) == 0) {
char name[256];
uint32_t type;
while (sgl_fs_readdir(dd, name, sizeof(name), &type) > 0) {
if (type & SGL_S_IFDIR) {
printf("[DIR] %s\n", name);
} else {
printf("[FILE] %s\n", name);
}
}
sgl_fs_closedir(dd);
}
1.6 其他操作¶
/* 获取文件状态 */
sgl_stat_t st;
sgl_fs_stat("/test.txt", &st);
printf("Size: %u\n", st.st_size);
/* 删除文件 */
sgl_fs_remove("/test.txt");
/* 重命名 */
sgl_fs_rename("/old.txt", "/new.txt");
/* 获取文件系统空间信息 */
sgl_statvfs_t vfs;
sgl_fs_statvfs("/", &vfs);
printf("Total: %u blocks, Free: %u blocks\n", vfs.f_blocks, vfs.f_bfree);
/* 卸载文件系统 */
sgl_fs_unmount("/");
1.7 注意事项¶
FATFS 支持 MBR 分区表,会自动检测并跳转到第一个 FAT 分区
支持 FAT12/FAT16/FAT32,自动识别
支持长文件名(LFN),最大 32 个 Unicode 字符
扇区大小必须是 2 的幂,通常为 512 字节
格式化使用
sgl_fs_format("/", NULL),会创建 FAT16 文件系统
2. LittleFS — 面向 Flash 的轻量日志文件系统¶
LittleFS 是 SGL 自带的轻量级日志文件系统(兼容 littlefs v2 设计),专为 Flash 存储设计,具有掉电安全和磨损均衡特性。
2.1 底层接口对接¶
LittleFS 同样通过 sgl_block_dev_t 与底层设备交互,接口与 FATFS 相同。但 LittleFS 需要设备提供擦除块大小信息。
参考 附件 中的 NOR Flash 对接示例:
#include "../bsp/norflash/norflash.h"
/* 1. 设备初始化 */
int __norflash_init(struct sgl_block_dev *dev)
{
norflash_init();
return 0;
}
/* 2. 读扇区(NOR Flash 扇区大小为 4096 字节) */
int __norflash_read_sectors(struct sgl_block_dev *dev, uint32_t sector, uint8_t *buf, uint32_t count)
{
uint32_t i;
for (i = 0; i < count; i++) {
norflash_read(buf + (i * 4096), sector * 4096 + (i * 4096), 4096);
}
return 0;
}
/* 3. 写扇区 */
int __norflash_write_sectors(struct sgl_block_dev *dev, uint32_t sector, const uint8_t *buf, uint32_t count)
{
uint32_t i;
for (i = 0; i < count; i++) {
norflash_write((uint8_t *)buf + (i * 4096), sector * 4096 + (i * 4096), 4096);
}
return 0;
}
/* 4. 设备信息(必须提供 erase_block_size 或 block_size) */
const sgl_block_dev_info_t norflash_info = {
.sector_count = 4096, // 总扇区数
.sector_size = 4096, // 每扇区 4096 字节
.block_size = 1, // 擦除块 = 1 个扇区
.erase_block_size = 4096, // 擦除块大小 4096 字节
.page_size = 256, // 编程页大小
};
/* 5. 块设备实例 */
const sgl_block_dev_t norflash_block_dev = {
.init = __norflash_init,
.read_sectors = __norflash_read_sectors,
.write_sectors = __norflash_write_sectors,
.info = &norflash_info,
};
2.2 设备信息要求¶
LittleFS 在挂载时会自动检测设备信息,按以下优先级确定文件系统块大小:
ioctl(SGL_BLK_GET_BLOCK_SIZE)返回的块大小(扇区数)× 扇区大小dev->info->erase_block_size(擦除块大小,字节)dev->info->block_size × sector_size如果以上都不可用,默认使用
sector_size(512 字节)
注意:对于 NOR Flash,建议在
info中正确设置erase_block_size或block_size,否则 LittleFS 会使用 512 字节作为块大小,可能导致性能下降。
2.3 注册、格式化与挂载¶
LittleFS 在挂载时会自动检测设备上的超级块。对于全新的 Flash 设备,需要先格式化才能挂载。格式化有两种方式:
方式一:挂载时自动格式化(推荐)
#include "../sgl/fs/littlefs/sgl_littlefs.h"
/* 1. 注册 LittleFS 文件系统类型 */
sgl_littlefs_register();
/* 2. 挂载(首次挂载未格式化的设备会自动格式化) */
sgl_fs_mount("/flash", "littlefs", &norflash_block_dev, NULL);
挂载时如果检测到无效的超级块(magic 不正确),会自动调用
sgl_fs_format()格式化设备。这是最简便的方式。
方式二:先手动格式化,再挂载
/* 1. 注册 LittleFS */
sgl_littlefs_register();
/* 2. 先挂载(自动格式化失败时挂载会返回错误,但 ctx 已创建)*/
/* 对于全新设备,直接用方式一即可自动格式化 */
/* 如果需要强制重建,先挂载再单独格式化: */
sgl_fs_mount("/flash", "littlefs", &norflash_block_dev, NULL);
/* 3. 单独格式化(会调用已挂载文件系统的 format 函数) */
sgl_fs_format("/flash", NULL);
/* 4. 格式化后文件系统已重建,可直接使用 */
如果自动格式化失败,通常是因为设备信息(
sector_count、erase_block_size等)未正确配置。请检查sgl_block_dev_info_t中的字段是否填写完整。
2.4 文件读写示例¶
/* 写文件 */
int fd = sgl_fs_open("/flash/config.txt", SGL_O_WRONLY | SGL_O_CREAT | SGL_O_TRUNC);
if (fd >= 0) {
const char *data = "{\"wifi\":\"SSID\",\"password\":\"123456\"}";
sgl_fs_write(fd, data, strlen(data));
sgl_fs_close(fd);
}
/* 读文件 */
fd = sgl_fs_open("/flash/config.txt", SGL_O_RDONLY);
if (fd >= 0) {
char buf[256] = {0};
sgl_fs_read(fd, buf, sizeof(buf) - 1);
printf("Config: %s\n", buf);
sgl_fs_close(fd);
}
2.5 目录操作¶
/* 创建目录 */
sgl_fs_mkdir("/flash/data");
/* 遍历目录 */
int dd;
if (sgl_fs_opendir("/flash", &dd) == 0) {
char name[256];
uint32_t type;
while (sgl_fs_readdir(dd, name, sizeof(name), &type) > 0) {
printf("%s: %s\n", (type & SGL_S_IFDIR) ? "[DIR]" : "[FILE]", name);
}
sgl_fs_closedir(dd);
}
2.6 注意事项¶
LittleFS 是日志结构文件系统,写入不需要先擦除,直接覆盖即可
具有掉电安全特性,异常断电不会损坏文件系统
文件系统块大小由设备信息自动确定,建议与 Flash 的擦除块大小一致
文件名最大长度 255 字节
支持目录层级,路径分隔符为
/首次挂载未格式化的设备时会自动格式化;也可先调用
sgl_fs_format()手动格式化后再挂载如果自动格式化失败,请检查设备
info中的sector_count、erase_block_size等字段是否正确
3. RAMFS — 基于内存的临时文件系统¶
RAMFS 是完全基于 RAM 的文件系统,所有数据存储在堆内存中,掉电即丢失。适用于临时文件、配置文件缓存、运行时数据等场景。
3.1 特点¶
无需底层设备对接:RAMFS 不需要
sgl_block_dev_t,挂载时传入NULL动态内存分配:文件数据通过
sgl_malloc/sgl_free动态管理速度快:纯内存操作,读写速度极快
容量限制:受可用堆内存限制
3.2 注册与挂载¶
#include "../sgl/fs/ramfs/sgl_ramfs.h"
/* 1. 注册 RAMFS 文件系统类型 */
sgl_ramfs_register();
/* 2. 挂载 RAMFS(dev 参数传 NULL) */
sgl_fs_mount("/tmp", "ramfs", NULL, NULL);
挂载配置参数(可选):
typedef struct {
uint32_t max_size; // 最大文件数据字节数(0 = 不限制,直到堆耗尽)
} sgl_ramfs_config_t;
// 限制 RAMFS 最大使用 64KB
sgl_ramfs_config_t ramfs_cfg = {
.max_size = 64 * 1024,
};
sgl_fs_mount("/tmp", "ramfs", NULL, &ramfs_cfg);
3.3 文件读写示例¶
/* 写文件 */
int fd = sgl_fs_open("/tmp/hello.txt", SGL_O_WRONLY | SGL_O_CREAT | SGL_O_TRUNC);
if (fd >= 0) {
const char *data = "Hello, RAMFS!";
sgl_fs_write(fd, data, strlen(data));
sgl_fs_close(fd);
}
/* 读文件 */
fd = sgl_fs_open("/tmp/hello.txt", SGL_O_RDONLY);
if (fd >= 0) {
char buf[64] = {0};
sgl_fs_read(fd, buf, sizeof(buf) - 1);
printf("Read: %s\n", buf);
sgl_fs_close(fd);
}
/* 追加写入 */
fd = sgl_fs_open("/tmp/hello.txt", SGL_O_WRONLY | SGL_O_APPEND);
if (fd >= 0) {
sgl_fs_write(fd, "\nAppended line.", 15);
sgl_fs_close(fd);
}
3.4 目录操作¶
/* 创建目录结构 */
sgl_fs_mkdir("/tmp/cache");
sgl_fs_mkdir("/tmp/session");
/* 遍历根目录 */
int dd;
if (sgl_fs_opendir("/tmp", &dd) == 0) {
char name[256];
uint32_t type;
while (sgl_fs_readdir(dd, name, sizeof(name), &type) > 0) {
printf("%s: %s\n", (type & SGL_S_IFDIR) ? "[DIR]" : "[FILE]", name);
}
sgl_fs_closedir(dd);
}
3.5 典型应用场景¶
/* 场景 1:缓存从 SD 卡读取的配置文件 */
int src = sgl_fs_open("/config.ini", SGL_O_RDONLY); // 从 FATFS 读取
if (src >= 0) {
int dst = sgl_fs_open("/tmp/config.ini", SGL_O_WRONLY | SGL_O_CREAT | SGL_O_TRUNC); // 缓存到 RAMFS
if (dst >= 0) {
char buf[512];
int n;
while ((n = sgl_fs_read(src, buf, sizeof(buf))) > 0) {
sgl_fs_write(dst, buf, n);
}
sgl_fs_close(dst);
}
sgl_fs_close(src);
}
/* 场景 2:运行时生成临时数据 */
int fd = sgl_fs_open("/tmp/sensor_log.txt", SGL_O_WRONLY | SGL_O_CREAT | SGL_O_APPEND);
if (fd >= 0) {
char line[64];
snprintf(line, sizeof(line), "temp=%.1f humi=%.1f\n", 25.5, 60.0);
sgl_fs_write(fd, line, strlen(line));
sgl_fs_close(fd);
}
3.6 注意事项¶
RAMFS 不需要块设备,挂载时
dev参数传NULL所有数据存储在堆内存中,系统重启后数据丢失
文件名最大长度 31 字节(由
SGL_RAMFS_MAX_NAME_LEN定义)可通过
max_size配置限制最大内存使用量,防止内存耗尽删除文件会自动释放内存
4. 附录:VFS API 速查¶
4.1 文件系统注册与挂载¶
函数 |
说明 |
|---|---|
|
注册 FATFS 文件系统类型 |
|
注册 LittleFS 文件系统类型 |
|
注册 RAMFS 文件系统类型 |
|
挂载文件系统到指定路径 |
|
卸载文件系统 |
4.2 文件操作¶
函数 |
说明 |
|---|---|
|
打开文件,返回文件描述符 |
|
关闭文件 |
|
读取文件数据 |
|
写入文件数据 |
|
删除文件或空目录 |
|
重命名文件或目录 |
|
获取文件状态信息 |
打开模式标志:
标志 |
说明 |
|---|---|
|
只读打开 |
|
只写打开 |
|
读写打开 |
|
文件不存在时创建 |
|
打开时截断文件为 0 长度 |
|
追加模式写入 |
4.3 目录操作¶
函数 |
说明 |
|---|---|
|
打开目录,返回目录描述符 |
|
读取目录项(>0 成功,0 结束) |
|
关闭目录 |
|
创建目录 |
4.4 其他操作¶
函数 |
说明 |
|---|---|
|
同步文件系统(刷新缓存) |
|
格式化文件系统 |
|
获取文件系统空间信息 |
4.5 完整示例:同时使用三种文件系统¶
#include <sgl_fs.h>
#include "../sgl/fs/fatfs/sgl_fatfs.h"
#include "../sgl/fs/littlefs/sgl_littlefs.h"
#include "../sgl/fs/ramfs/sgl_ramfs.h"
// 外部声明的块设备
extern const sgl_block_dev_t sdcard_block_dev;
extern const sgl_block_dev_t norflash_block_dev;
void fs_demo(void)
{
/* 注册所有文件系统类型 */
sgl_fatfs_register();
sgl_littlefs_register();
sgl_ramfs_register();
/* 挂载三个文件系统到不同路径 */
sgl_fs_mount("/sd", "fatfs", &sdcard_block_dev, NULL); // SD 卡 -> FATFS
sgl_fs_mount("/flash","littlefs", &norflash_block_dev, NULL); // NOR Flash -> LittleFS
sgl_fs_mount("/tmp", "ramfs", NULL, NULL); // 内存 -> RAMFS
/* 从 SD 卡读取配置文件,缓存到 RAMFS */
int src = sgl_fs_open("/sd/config.ini", SGL_O_RDONLY);
if (src >= 0) {
int dst = sgl_fs_open("/tmp/config.ini", SGL_O_WRONLY | SGL_O_CREAT | SGL_O_TRUNC);
if (dst >= 0) {
char buf[256];
int n;
while ((n = sgl_fs_read(src, buf, sizeof(buf))) > 0) {
sgl_fs_write(dst, buf, n);
}
sgl_fs_close(dst);
}
sgl_fs_close(src);
}
/* 列出 SD 卡根目录 */
int dd;
if (sgl_fs_opendir("/sd", &dd) == 0) {
char name[256];
uint32_t type;
printf("SD Card files:\n");
while (sgl_fs_readdir(dd, name, sizeof(name), &type) > 0) {
printf(" %s %s\n", (type & SGL_S_IFDIR) ? "[DIR]" : "[FILE]", name);
}
sgl_fs_closedir(dd);
}
/* 卸载 */
sgl_fs_unmount("/tmp");
sgl_fs_unmount("/flash");
sgl_fs_unmount("/sd");
}
参考文件:
#include <sgl_fs.h>
#include <stdint.h>
int sdcard_init(struct sgl_block_dev *dev)
{
return (sdio_sd_init() == SD_OK) ? 0 : -1;
}
int sdio_sd_read_sectors(struct sgl_block_dev *dev, uint32_t sector, uint8_t *buf, uint32_t count)
{
return (sd_read_disk(buf, sector, count) == SD_OK) ? 0 : -1;
}
int sdio_sd_write_sectors(struct sgl_block_dev *dev, uint32_t sector, const uint8_t *buf, uint32_t count)
{
return (sd_write_disk((uint8_t *)buf, sector, count) == SD_OK) ? 0 : -1;
}
const static sgl_block_dev_info_t sdcard_info = {
.sector_size = 512,
.block_size = 0,
.erase_block_size = 0,
.page_size = 512,
};
const sgl_block_dev_t sdcard_block_dev = {
.init = sdcard_init,
.read_sectors = sdio_sd_read_sectors,
.write_sectors = sdio_sd_write_sectors,
.info = &sdcard_info,
};
int __norflash_init(struct sgl_block_dev *dev)
{
norflash_init();
return 0;
}
int __norflash_read_sectors(struct sgl_block_dev *dev, uint32_t sector, uint8_t *buf, uint32_t count)
{
uint32_t i;
/* NOR flash sector size is 4096 bytes */
for (i = 0; i < count; i++)
{
norflash_read(buf + (i * 4096), sector * 4096 + (i * 4096), 4096);
}
return 0;
}
int __norflash_write_sectors(struct sgl_block_dev *dev, uint32_t sector, const uint8_t *buf, uint32_t count)
{
uint32_t i;
/* NOR flash sector size is 4096 bytes */
for (i = 0; i < count; i++)
{
norflash_write((uint8_t *)buf + (i * 4096), sector * 4096 + (i * 4096), 4096);
}
return 0;
}
const sgl_block_dev_t norflash_block_dev = {
.init = __norflash_init,
.read_sectors = __norflash_read_sectors,
.write_sectors = __norflash_write_sectors,
.info = &sgl_w25q128_info,
};