CN / EN
CN / EN

写技术文章

APP FS文件系统框架介绍

东风永恒

汇顶员工
2022-07-11 19:25:09
1. 简介

在最新的GR5xx SDK中,实现了一套文件系统框架APP FS, 类似于Linux下的虚拟文件系统VFS, 向上提供一套标准的文件操作接口,向下提供适配方法。

一个具体的文件系统(如littlefs、fatfs)需要被支持,只需按照框架的接口规范提供其底层文件系统操作函数,并注册到APP FS。

APP FS支持的特性:

  • 兼容posix的文件API;
  • 支持多文件系统;
  • 支持多挂载点。


APP FS源码存放于:“{SDK}/app/components/libraries/app_fs”。

2. 标准接口

APP FS向用户提供了统一的文件操作API, 可分为磁盘操作、文件操作、目录操作三大类。

  • 磁盘操作API:
操作函数 函数功能
int app_fs_mount(const char *target, const char *fs_name, uint32_t mountflags, const void *fs_ops) 挂载文件系统
int app_fs_umount(const char *target) 卸载文件系统
int app_fs_format(const char *dev, const char *fs_name, void *data) 格式化磁盘
int app_fs_fdisk(const char *dev, const char *fs_name, int *length_array, int partnum) 获取磁盘信息
  • 文件操作API:
操作函数 函数功能
int app_fs_open(const char *path, int flags) 打开文件
int app_fs_close(int fd) 关闭文件
size_t app_fs_read(int fd, char *buff, size_t bytes) 从指定文件中读数据
size_t app_fs_write(int fd, const void *buff, size_t bytes) 向指定文件写数据
size_t app_fs_lseek(int fd, int32_t off, int whence) 定位到文件索引
int app_fs_stat(const char *path, fs_stat_t *stat) 获取文件信息
int app_fs_unlink(const char *path) 删除指定路径文件
int app_fs_rename(const char *old, const char *new) 文件重命名
int app_fs_sync(int fd) 文件操作同步
  • 目录操作API:
操作函数 函数功能
fs_dir_t *app_fs_opendir(const char *path) 打开指定路径目录
struct dirent *app_fs_readdir(fs_dir_t *d) 读取目录信息
int app_fs_closedir(fs_dir_t *d) 关闭一个目录
int app_fs_mkdir(const char *path, int mode) 创建一个目录
int app_fs_rmdir(const char *path) 删除指定路径目录

3. 文件系统适配

APP FS底层不限制具体的文件系统种类,也不限制文件系统的数量,因为一个系统中可能需要存在多种不同的文件系统。本章节主要以在小型嵌入式设备上使用非常广泛的Littlefs为例,指导如何将其适配到APP FS中。

3.1 Littlefs简介

Littlefs是一个为微控制器设计的安全文件系统,常用于nor flash上,具有以下特点:

  • 掉电恢复能力: 设计用于处理随机电源故障。所有文件操作都有很强的写时拷贝保证,如果断电,文件系统将恢复到上一次已知的良好状态。
  • 动态磨损均衡: 设计考虑到闪存设备的特点,提供动态块磨损均衡。
  • 占用RAM/ROM少: 设计严格控制RAM的使用,且RAM的使用不会随着文件系统的增长而改变。文件系统不包含无界递归,动态内存仅限于可静态提供的可配置缓冲区。

3.2 Littlefs适配

Littlefs开源地址:https://github.com/littlefs-project/littlefs/tags

Littlefs目前最新版本为v2.5.0,GR5xx SDK使用v2.5.0的源码并存放于“{SDK}/app/external/littlefs”。

3.2.1 抽象文件系统操作函数

APP FS对底层文件操作进行了抽象,分别定义了文件操作、挂载操作、磁盘管理三大类文件系统作操函数的operation结构体。

  • mount operation结构体
typedef struct mount_ops {
int (*mount)(struct fs_mp *mp, unsigned long mountflags, const void *ops);
int (*umount)(struct fs_mp *mp);
int (*statfs)(const char *path, fs_statfs_t *buf);
} mount_ops_t;
  • file operation结构体
typedef struct file_ops {
int (*open)(struct file *, const char *, int);
int (*close)(struct file *);
size_t (*read)(struct file *, char *, size_t);
size_t (*write)(struct file *, const char *, size_t);
size_t (*lseek)(struct file *, int32_t, int);
int (*stat)(struct fs_mp *, const char *, struct _stat *);
int (*unlink)(struct fs_mp *, const char *);
int (*rename)(struct fs_mp *, const char *, const char *);
int (*ioctl)(struct file *, int, unsigned long);
int (*sync)(struct file *);
int (*opendir)(struct dir *, const char *);
int (*readdir)(struct dir *, struct dirent *);
int (*closedir)(struct dir *);
int (*mkdir)(struct fs_mp *, const char *);
int (*rmdir)(struct fs_mp *, const char *);
} file_ops_t;
  • management operation结构体
typedef struct mgmt_ops {
int (*fdisk)(const char *dev, int *length_array, int part_num);
int (*format)(const char *dev, void *data);
} mgmt_ops_t;

APP FS提供一个文件系统的注册函数,函数原型如下:

int app_fs_register(const char *fs_name, mount_ops_t *mount_ops, file_ops_t *file_ops, mgmt_ops_t *mgmt_ops)

  1. fs_name: 当前需要注册的文件系统的名字字符串,可以自定义,如“littlefs”“fatfs”等;
  2. mount_ops: 即指向用户定义的mount operation结构体,结构体成员的函数指针需要指向用户根据具体的文件系统适配的挂载、卸载操作接口。
  3. file_ops: 即指向用户定义的file operation结构体,结构体成员的函数指针需要指向用户根据具体的文件系统适配的文件操作接口。
  4. mgmt_ops: 即指向用户定义的文件系统management operation结构体,结构体成员的函数指针需要指向用户根据具体的文件系统适配的文件系统管理操作接口。

用户在适配完成一个文件系统后,需要调用app_fs_register函数将operation结构体信息注册到文件系统信息链表中,当应用程序调用app_fs文件系统操作API时,内部会从文件系统信息链表中找到operation结构体对应的文件系统操作函数,以app_fs_open接口为例,整个链路如下图所示:


3.2.2 适配步骤

上一小节主要介绍了对抽象函数operation结构体的认识,实际上文件系统的适配工作就是对这些抽象函数进行具体化实现。

3.2.2.1 文件操作函数实现

新建一个lfs_adapter.c文件,分别定义g_lfs_mount_ops、g_lfs_file_ops、g_lfs_mgmt,实现如下所示 :

static struct mount_ops g_lfs_mount_ops = {
.mount = __lfs_mount,
.umount = __lfs_umount,
.statfs = NULL,
};
static struct file_ops g_lfs_file_ops = {
.open = __lfs_open,
.close = __lfs_close,
.read = __lfs_read,
.write = __lfs_write,
.lseek = __lfs_seek,
.stat = __lfs_stat,
.unlink = __lfs_unlink,
.rename = __lfs_rename,
.ioctl = NULL, /* not support */
.sync = __lfs_sync,
.opendir = __lfs_open_dir,
.readdir = __lfs_read_dir,
.closedir = __lfs_close_dir,
.mkdir = __lfs_mkdir,
.rmdir = __lfs_rmdir,
};
static struct mgmt_ops g_lfs_mgmt = {
.fdisk = NULL,
.format = NULL,
};

根据littlefs的文件系统操作API,封装实现__lfs_mount、__lfs_open等操作函数,如下代码段所示:

static int __lfs_open(struct file *file, const char *path_name, int flag)
{
int ret;

if ((path_name == NULL) || (file == NULL) || (file->mp == NULL) ||
(file->mp->data == NULL))
{
return FS_ERROR;
}

lfs_file_t *lfs_file = (lfs_file_t *)fs_mem_malloc(sizeof(lfs_file_t));
if (file == NULL)
return FS_ERROR;

int lfs_open_flag = convert_to_lfs_flag(flag);
ret = lfs_file_open((lfs_t *)file->mp->data, lfs_file, path_name, lfs_open_flag);
if (ret != 0)
{
fs_mem_free(lfs_file);
}
else
{
file->data = (void *)lfs_file;
}

return ret;
}

3.2.2.2 文件系统注册

APP FS支持挂载多个文件系统,适配的文件系统信息需要注册到内部的管理链表,需调用app_fs_register进行注册。

void lfs_init(void)
{
app_fs_register("littlefs", &g_lfs_mount_ops, &g_lfs_file_ops, &g_lfs_mgmt);
}

3.2.2.3 底层存储介质操作

文件系统会将文件的数据内容存储在存储介质上,例如flash、nand、SD卡等,而这些存储介质的底层操作实现又有很大的差异。为了和APP FS框架解耦,将存储介质的读、写、擦操作函数放在应用层代码实现,函数地址和一些配置参数封装在 lfs_ops结构体中,在app_fs_mount进行文件系统挂载的时候传入文件系统内部。

Littlefs定义了一个lfs_config结构体,结构体成员函数指针需要指向用户适配的存储介质读、写、擦操作函数,并配置存储介质操作块大小、块个数、和cache size等参数。

struct lfs_config {
void *context;
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);

lfs_size_t read_size;
lfs_size_t prog_size;
lfs_size_t block_size;
lfs_size_t block_count;
};

在lfs_adapter.h中定义lfs_ops结构体, cfg记录Littlefs的配置信息,start_addr记录当前存储介质起始地址:

typedef struct {
struct lfs_config cfg; /**< Littlfs config structures */
uint32_t start_addr; /**< The starting address of the storage space by the file system */
} lfs_ops_t;

操作存储介质的读、写、擦实现可参考fs_demo示例工程,介质读取函数实现如下代码段所示:

int lfs_block_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)
{
uint32_t addr;
lfs_ops_t *lfs_ops = (lfs_ops_t *)cfg;

addr = lfs_ops->start_addr + block * cfg->block_size + off;
return size == hal_flash_read(addr, (uint8_t*)buffer, size) ? LFS_ERR_OK : LFS_ERR_IO;
}

应用程序在挂载文件系统时,将lfs_ops传入底层的Littlefs文件系统:

static lfs_ops_t s_lfs0_ops;
void fs_mount_point0(const char *target)
{
s_lfs0_ops.cfg.read = lfs_block_read;
s_lfs0_ops.cfg.prog = lfs_block_prog;
s_lfs0_ops.cfg.erase = lfs_block_erase;
s_lfs0_ops.cfg.sync = lfs_device_sync;
s_lfs0_ops.cfg.read_size = 16;
s_lfs0_ops.cfg.prog_size = 16;
s_lfs0_ops.cfg.block_size = 4096;
s_lfs0_ops.cfg.block_count = LFS0_FLASH_SIZE / 4096;
s_lfs0_ops.cfg.block_cycles = 500;
s_lfs0_ops.cfg.cache_size = 16;
s_lfs0_ops.cfg.lookahead_size = 32;
s_lfs0_ops.start_addr = LFS0_FLASH_START_ADDR;

app_fs_mount(target, "littlefs", MS_FORMAT, &s_lfs0_ops);
}

4. APP FS应用

应用层使用APP FS,主要关注两点:

  1. 存储介质的读、写、操操作函数的实现。 一个文件系统底层操作的存储介质可能会根据实际的项目需求多种多样,所以存储介质的操作适配放在应用层实现,然后注册到文件系统。
  2. APP FS文件操作函数的使用方法。 APP FS的文件操作API使用方法类似posix的文件操作,如open、read、write,例如app_fs_open返回的是文件操作描述符fd, app_fs_write传入的文件对象为fd而不是一个文件路径。

APP FS使用流程如下图所示:

更详尽的APP FS示例Demo请参见“{SDK}/app/projects/test/components/app_fs_rtos”。

1收藏

2赞成

您的评论
我们时刻倾听您的声音
联系销售

扫描关注公众号

打开微信,使用“扫一扫”即可关注