# 文件系统 ## 概述 该组件是ARCS SDK提供的文件系统框架,支持多种文件系统类型和存储介质。该组件采用分层架构设计,提供了从底层磁盘访问到高层POSIX兼容接口的完整解决方案。 ## 架构设计 FS组件采用分层架构设计,数据流向:应用层 → POSIX API/LSFS API → LVFS → LSFS → SubFS → Disk ``` ┌──────────────────────────────────────────────────────────┐ │ 应用层 (Application Layer) │ │ 支持两种调用方式: │ │ 1. POSIX API (open/read/write/close) │ │ 2. LSFS API (lsfs_open/lsfs_read/lsfs_write) │ └──────────────────────────────────────────────────────────┘ ↓ ↓ ┌───────────────┐ ┌───────────────┐ │ POSIX API │ │ 直接调用 │ └───────────────┘ └───────────────┘ ↓ ↓ ┌──────────────────────────────────┐ │ │ LVFS (LiSa Virtual File System) │ │ │ - 提供POSIX接口实现 │ │ │ - 文件描述符管理 │ │ │ - 内部调用LSFS接口 │ │ └──────────────────────────────────┘ │ ↓ ↓ ┌──────────────────────────────────────────────────────────┐ │ LSFS (LiSa File System) │ │ - 文件系统抽象层,提供统一接口 │ │ - 多挂载点管理 │ │ - 静态注册SubFS中的文件系统实例 │ │ - 冻结/恢复功能(用于系统掉电保护) │ └──────────────────────────────────────────────────────────┘ ↓ ┌──────────────────────────────────────────────────────────┐ │ SubFS (子文件系统实现层) │ │ ┌──────────────────────────────────────────────┐ │ │ │ FatFS - FAT文件系统实现 │ │ │ │ (通过lsfs_register注册到LSFS) │ │ │ └──────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────┘ ↓ ┌──────────────────────────────────────────────────────────┐ │ Disk 驱动层 (Disk Driver Layer) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ RAM Disk │ │Flash Disk│ │ SD/MMC │ │ │ └──────────┘ └──────────┘ └──────────┘ │ └──────────────────────────────────────────────────────────┘ ``` ### 组件说明 #### 1. 应用层调用方式 应用层支持两种文件系统操作方式: **方式一:使用POSIX API** ```c int fd = open("/RAM:/test.txt", O_CREAT | O_RDWR, 0666); write(fd, data, size); read(fd, buffer, size); close(fd); ``` - POSIX API调用会经过LVFS层,LVFS内部转发给LSFS处理 - 适合需要标准POSIX兼容的应用 **方式二:直接使用LSFS API** ```c struct lsfs_file_t file; lsfs_file_t_init(&file); lsfs_open(&file, "/RAM:/test.txt", LSFS_O_CREATE | LSFS_O_RDWR); lsfs_write(&file, data, size); lsfs_read(&file, buffer, size); lsfs_close(&file); ``` - 直接调用LSFS接口,不经过LVFS层 - 更轻量级,适合资源受限的嵌入式应用 #### 2. LSFS (LiSa File System) LSFS是轻量级文件系统抽象层,提供统一的文件系统接口。SubFS中的文件系统实例(如FatFS)在初始化时通过`lsfs_register()`静态注册到LSFS。 **特性:** - 支持多文件系统类型(FatFS等) - 多挂载点管理 - 统一的错误处理 - 文件系统格式化支持 - 冻结/恢复功能(用于系统掉电保护) - SubFS静态注册机制 **主要API:** - 文件操作:`lsfs_open()`, `lsfs_close()`, `lsfs_read()`, `lsfs_write()` - 目录操作:`lsfs_mkdir()`, `lsfs_opendir()`, `lsfs_readdir()` - 文件管理:`lsfs_unlink()`, `lsfs_rename()`, `lsfs_stat()` - 挂载管理:`lsfs_mount()`, `lsfs_unmount()`, `lsfs_mkfs()` - 文件系统注册:`lsfs_register()`, `lsfs_unregister()` #### 3. LVFS (LiSa Virtual File System) LVFS提供POSIX兼容接口,内部调用LSFS接口实现功能。应用层通过POSIX API(如`open()`, `read()`等)操作文件时,实际由LVFS转发给LSFS处理。 **特性:** - 提供POSIX API实现 - 文件描述符管理 - 内部转发调用到LSFS - 支持大文件(64位偏移) **主要API:** - `lvfs_open()`, `lvfs_close()`, `lvfs_read()`, `lvfs_write()` - `lvfs_lseek()`, `lvfs_truncate()`, `lvfs_sync()` - `lvfs_register()` - 注册文件系统到LVFS层 #### 4. SubFS (子文件系统) SubFS包含具体的文件系统实现,通过`lsfs_register()`注册到LSFS。 **支持的文件系统:** - **FatFS** - FAT文件系统实现,在`lsfs_fat_init()`中注册 **注册机制:** ```c // FatFS初始化时静态注册到LSFS int lsfs_fat_init(void) { return lsfs_register(LSFS_FATFS, &fatfs_fs); } ``` #### 5. Disk 驱动层 提供对各种存储介质的抽象访问,被SubFS中的文件系统实现所依赖。 **支持的存储类型:** - **RAM Disk** - 内存模拟磁盘 - **Flash Disk** - Flash存储 - **SD/MMC** - SD卡存储 **主要API:** - `disk_init()` - 初始化磁盘子系统 - `disk_access_init()` - 初始化特定磁盘 - `disk_access_read()` - 读取扇区 - `disk_access_write()` - 写入扇区 - `disk_access_ioctl()` - 磁盘控制操作 ## 配置选项 在 `menuconfig` 中配置文件系统: ``` File Systems ---> [*] File system support [*] Support __LARGE64_FILES SubFS ---> [*] FatFS file system support LSFS ---> [*] Enable LSFS [*] Support freeze functionality LVFS ---> [*] Enable LVFS [*] Enable POSIX API alias Disk Driver ---> [*] RAM Disk support [*] Flash Disk support [*] SD/MMC support ``` ## 使用指南 ### 1. 使用LSFS接口 #### 基本流程 ```c #include "lsfs.h" #include "disk/disk_access.h" // 1. 初始化磁盘和文件系统 disk_init(NULL); lsfs_init(); // 2. 定义挂载点 static struct lsfs_mount_t ram_mnt = { .type = LSFS_FATFS, .mnt_point = "/RAM:", .fs_data = NULL, }; // 3. 挂载文件系统 int ret = lsfs_mount(&ram_mnt); if (ret != 0) { // 挂载失败,尝试格式化 ret = lsfs_mkfs(LSFS_FATFS, "RAM:", NULL, 0); if (ret == 0) { ret = lsfs_mount(&ram_mnt); } } // 4. 文件操作 struct lsfs_file_t file; lsfs_file_t_init(&file); // 写入文件 ret = lsfs_open(&file, "/RAM:/test.txt", LSFS_O_CREATE | LSFS_O_WRITE); if (ret == 0) { const char *data = "Hello, LSFS!"; lsfs_write(&file, data, strlen(data)); lsfs_close(&file); } // 读取文件 lsfs_file_t_init(&file); ret = lsfs_open(&file, "/RAM:/test.txt", LSFS_O_READ); if (ret == 0) { char buffer[64]; ssize_t bytes = lsfs_read(&file, buffer, sizeof(buffer) - 1); if (bytes > 0) { buffer[bytes] = '\0'; printf("Read: %s\n", buffer); } lsfs_close(&file); } // 5. 卸载文件系统 lsfs_unmount(&ram_mnt); ``` #### 打开模式标志 ```c // 访问模式 LSFS_O_READ // 只读模式 LSFS_O_WRITE // 只写模式 LSFS_O_RDWR // 读写模式 // 创建标志 LSFS_O_CREATE // 文件不存在则创建 LSFS_O_APPEND // 追加模式 LSFS_O_TRUNC // 打开时清空文件 ``` #### 目录操作 ```c #include "lsfs.h" void list_directory(const char *path) { struct lsfs_dir_t dir; struct lsfs_dirent entry; int ret; // 初始化目录对象 lsfs_dir_t_init(&dir); // 打开目录 ret = lsfs_opendir(&dir, path); if (ret == 0) { // 遍历目录 while (1) { ret = lsfs_readdir(&dir, &entry); if (ret != 0 || entry.name[0] == 0) break; if (entry.type == LSFS_DIR_ENTRY_DIR) { printf(" %s\n", entry.name); } else { printf("%10lu %s\n", entry.size, entry.name); } } lsfs_closedir(&dir); } } // 创建目录 ret = lsfs_mkdir("/RAM:/mydir"); // 删除文件或目录 ret = lsfs_unlink("/RAM:/test.txt"); // 重命名 ret = lsfs_rename("/RAM:/old.txt", "/RAM:/new.txt"); ``` #### 文件定位操作 ```c struct lsfs_file_t file; lsfs_file_t_init(&file); lsfs_open(&file, "/RAM:/data.bin", LSFS_O_RDWR); // 移动文件指针 lsfs_seek(&file, 100, LSFS_SEEK_SET); // 从文件开头偏移100字节 lsfs_seek(&file, 10, LSFS_SEEK_CUR); // 从当前位置前进10字节 lsfs_seek(&file, -20, LSFS_SEEK_END); // 从文件末尾倒退20字节 // 获取当前位置 off_t pos = lsfs_tell(&file); // 获取文件大小 off_t size = lsfs_lsize(&file); // 截断文件 lsfs_truncate(&file, 1024); // 截断或扩展到1024字节 // 同步到磁盘 lsfs_sync(&file); lsfs_close(&file); ``` #### 快速查找优化 对于大文件的频繁seek操作,可以使用快速查找功能: ```c struct lsfs_file_t file; lsfs_file_t_init(&file); lsfs_open(&file, "/RAM:/bigfile.bin", LSFS_O_READ); // 建立簇映射表(提升后续seek性能) lsfs_fastseek_link(&file, 1024); // 分配1024字节映射表 // 执行快速seek操作 lsfs_seek(&file, 1000000, LSFS_SEEK_SET); // 重建映射表(文件内容变化后) lsfs_fastseek_relinkmap(&file); // 释放映射表 lsfs_fastseek_unlink(&file); lsfs_close(&file); ``` ### 2. 使用POSIX兼容接口 通过LVFS层,可以使用标准POSIX API: ```c #include #include #include "lvfs.h" #include "lsfs.h" #include "disk/disk_access.h" // 1. 初始化 disk_init(NULL); lvfs_init(); lsfs_init(); // 2. 挂载文件系统 static struct lsfs_mount_t ram_mnt = { .type = LSFS_FATFS, .mnt_point = "/RAM:", .fs_data = NULL, }; lsfs_mount(&ram_mnt); // 3. 使用POSIX API int fd = open("/RAM:/test.txt", O_CREAT | O_RDWR, 0666); if (fd >= 0) { const char *data = "Hello, POSIX!"; write(fd, data, strlen(data)); lseek(fd, 0, SEEK_SET); char buffer[64]; ssize_t bytes = read(fd, buffer, sizeof(buffer) - 1); if (bytes > 0) { buffer[bytes] = '\0'; printf("Read: %s\n", buffer); } // 截断文件 ftruncate(fd, 100); // 同步到磁盘 fsync(fd); close(fd); } ``` ### 3. 工作目录管理 ```c #include "lsfs.h" // 改变当前工作目录 lsfs_chdir("/RAM:/mydir"); // 获取当前工作目录 char cwd[256]; lsfs_getcwd("/RAM:", cwd, sizeof(cwd)); printf("Current dir: %s\n", cwd); // 改变当前驱动器 lsfs_chdrive("/RAM:"); ``` ### 4. 文件系统信息查询 ```c #include "lsfs.h" // 获取文件或目录信息 struct lsfs_dirent entry; int ret = lsfs_stat("/RAM:/test.txt", &entry); if (ret == 0) { printf("Name: %s\n", entry.name); printf("Size: %lu bytes\n", entry.size); printf("Type: %s\n", entry.type == LSFS_DIR_ENTRY_FILE ? "File" : "Directory"); } // 获取文件系统容量信息 struct lsfs_statvfs stat; ret = lsfs_statvfs("/RAM:", &stat); if (ret == 0) { printf("Block size: %lu\n", stat.f_bsize); printf("Total blocks: %lu\n", stat.f_blocks); printf("Free blocks: %lu\n", stat.f_bfree); printf("Total size: %lu bytes\n", stat.f_blocks * stat.f_frsize); printf("Free size: %lu bytes\n", stat.f_bfree * stat.f_frsize); } ``` ### 5. 多存储介质支持 ```c #include "lsfs.h" #include "disk/disk_access.h" #include "lisa_sdmmc.h" // 初始化 disk_init(NULL); lsfs_init(); // RAM Disk static struct lsfs_mount_t ram_mnt = { .type = LSFS_FATFS, .mnt_point = "/RAM:", }; // Flash Disk static struct lsfs_mount_t flash_mnt = { .type = LSFS_FATFS, .mnt_point = "/NAND:", }; // SD Card static struct lsfs_mount_t sd_mnt = { .type = LSFS_FATFS, .mnt_point = "/SD:", }; // 初始化SD卡 lisa_sdmmc_probe(lisa_device_get("sdmmc0")); // 挂载各个存储设备 lsfs_mount(&ram_mnt); lsfs_mount(&flash_mnt); lsfs_mount(&sd_mnt); // 使用不同存储 struct lsfs_file_t file; // 在RAM上操作 lsfs_file_t_init(&file); lsfs_open(&file, "/RAM:/temp.txt", LSFS_O_CREATE | LSFS_O_WRITE); lsfs_write(&file, "RAM data", 8); lsfs_close(&file); // 在Flash上操作 lsfs_file_t_init(&file); lsfs_open(&file, "/NAND:/config.txt", LSFS_O_CREATE | LSFS_O_WRITE); lsfs_write(&file, "Flash data", 10); lsfs_close(&file); // 在SD卡上操作 lsfs_file_t_init(&file); lsfs_open(&file, "/SD:/media.bin", LSFS_O_CREATE | LSFS_O_WRITE); lsfs_write(&file, "SD data", 7); lsfs_close(&file); ``` ### 6. 系统掉电保护 LSFS提供冻结功能,在系统即将掉电时保护文件系统: ```c #if CONFIG_LSFS_SUPPORT_FREEZE // 系统掉电前调用 // 会自动关闭所有打开的文件,不修改文件系统结构 int ret = lsfs_freeze_freeze(0); if (ret < 0) { // 某些文件关闭失败 printf("Warning: Some files failed to close\n"); } #endif ``` ## 错误处理 所有API返回值遵循以下规则: - **成功**:返回 `0` 或正数(对于read/write返回字节数) - **失败**:返回负的errno值 常见错误码: ```c -EINVAL // 无效参数 -ENOENT // 文件或目录不存在 -EEXIST // 文件或目录已存在 -EROFS // 只读文件系统 -ENOSPC // 磁盘空间不足 -EBADF // 无效的文件描述符 -EBUSY // 资源忙 -ENOTSUP // 操作不支持 ``` 错误处理示例: ```c struct lsfs_file_t file; lsfs_file_t_init(&file); int ret = lsfs_open(&file, "/RAM:/test.txt", LSFS_O_READ); if (ret != 0) { switch (ret) { case -ENOENT: printf("File not found\n"); break; case -EINVAL: printf("Invalid path\n"); break; default: printf("Open failed: %d\n", ret); break; } return ret; } ``` ## 性能优化建议 ### 1. 文件操作优化 - **批量读写**:使用较大的缓冲区减少系统调用次数 ```c // 推荐:一次读取大块数据 char buffer[4096]; lsfs_read(&file, buffer, sizeof(buffer)); // 不推荐:频繁小块读取 char byte; for (int i = 0; i < 4096; i++) { lsfs_read(&file, &byte, 1); } ``` - **顺序访问**:避免频繁的随机seek,尽量顺序读写 - **及时关闭**:使用完文件后立即关闭,释放资源 ### 2. 大文件处理 - 使用64位文件接口处理大于2GB的文件: ```c #if CONFIG_FS_LARGE64_FILES _off64_t offset = lsfs_lseek64(&file, large_offset, LSFS_SEEK_SET); int64_t size = lsfs_lsize64(&file); lsfs_truncate64(&file, new_size); #endif ``` - 对大文件使用快速查找功能减少seek时间 ### 3. 磁盘缓存 某些磁盘驱动支持IO缓存,可提升性能(见 `iocache.c`) ### 4. 文件系统选择 - **RAM Disk**:速度最快,但掉电丢失,适合临时数据 - **Flash Disk**:持久化存储,适合配置文件 - **SD Card**:大容量,适合媒体文件 ## 示例程序 完整示例代码位于: - **LSFS示例**:`samples/modules/fs/lsfs/` - **POSIX示例**:`samples/modules/fs/posix/fs/` - **FatFS示例**:`samples/modules/fs/fatfs/` ## API参考 ### LSFS核心API | 函数 | 说明 | |------|------| | `lsfs_init()` | 初始化LSFS框架 | | `lsfs_mount()` | 挂载文件系统 | | `lsfs_unmount()` | 卸载文件系统 | | `lsfs_mkfs()` | 格式化文件系统 | | `lsfs_open()` | 打开或创建文件 | | `lsfs_close()` | 关闭文件 | | `lsfs_read()` | 读取文件 | | `lsfs_write()` | 写入文件 | | `lsfs_seek()` | 设置文件位置 | | `lsfs_tell()` | 获取文件位置 | | `lsfs_lsize()` | 获取文件大小 | | `lsfs_truncate()` | 截断文件 | | `lsfs_sync()` | 同步文件到磁盘 | | `lsfs_unlink()` | 删除文件或目录 | | `lsfs_rename()` | 重命名文件或目录 | | `lsfs_mkdir()` | 创建目录 | | `lsfs_opendir()` | 打开目录 | | `lsfs_readdir()` | 读取目录项 | | `lsfs_closedir()` | 关闭目录 | | `lsfs_stat()` | 获取文件信息 | | `lsfs_statvfs()` | 获取文件系统信息 | | `lsfs_getcwd()` | 获取当前目录 | | `lsfs_chdir()` | 改变当前目录 | | `lsfs_chdrive()` | 改变当前驱动器 | ### LVFS核心API | 函数 | 说明 | |------|------| | `lvfs_init()` | 初始化LVFS框架 | | `lvfs_register()` | 注册文件系统 | | `lvfs_unregister()` | 取消注册文件系统 | | `lvfs_open()` | 打开文件(返回fd) | | `lvfs_close()` | 关闭文件 | | `lvfs_read()` | 读取文件 | | `lvfs_write()` | 写入文件 | | `lvfs_lseek()` | 设置文件位置 | | `lvfs_truncate()` | 截断文件 | | `lvfs_sync()` | 同步文件 | ### Disk Access API | 函数 | 说明 | |------|------| | `disk_init()` | 初始化磁盘子系统 | | `disk_access_init()` | 初始化指定磁盘 | | `disk_access_status()` | 获取磁盘状态 | | `disk_access_read()` | 读取磁盘扇区 | | `disk_access_write()` | 写入磁盘扇区 | | `disk_access_ioctl()` | 磁盘控制操作 | | `disk_access_register()` | 注册磁盘驱动 | | `disk_access_unregister()` | 取消注册磁盘驱动 | ## 注意事项 1. **初始化顺序** - 先调用 `disk_init()` - 再调用 `lsfs_init()` 或 `lvfs_init()` - 最后调用 `lsfs_mount()` 2. **文件对象初始化** - 每次使用前必须调用 `lsfs_file_t_init()` 或 `lsfs_dir_t_init()` - 关闭后可以重用,但需重新初始化 3. **挂载点命名** - 必须以 `/` 开头 - 通常以 `:` 结尾(如 `/RAM:`) - 文件路径格式:`/RAM:/dir/file.txt` 4. **只读文件系统** - 挂载时可设置 `LSFS_MOUNT_FLAG_READ_ONLY` - 只读模式下写操作返回 `-EROFS` 5. **线程安全** - LSFS/LVFS API 是线程安全的 - 但同一个文件对象不应在多线程间共享 6. **资源限制** - 最大打开文件数由配置决定 - 文件名长度限制:`MAX_FILE_NAME` (默认256) ## 常见问题 ### Q1: 挂载失败怎么办? **A:** 挂载失败通常是因为文件系统未格式化,解决方法: ```c int ret = lsfs_mount(&mnt); if (ret != 0) { // 尝试格式化 ret = lsfs_mkfs(mnt.type, &mnt.mnt_point[1], NULL, 0); if (ret == 0) { ret = lsfs_mount(&mnt); // 重新挂载 } } ``` ### Q2: 如何在不同存储间复制文件? **A:** 打开源文件和目标文件,循环读写: ```c struct lsfs_file_t src, dst; char buffer[512]; ssize_t bytes; lsfs_file_t_init(&src); lsfs_file_t_init(&dst); lsfs_open(&src, "/RAM:/source.txt", LSFS_O_READ); lsfs_open(&dst, "/SD:/dest.txt", LSFS_O_CREATE | LSFS_O_WRITE); while ((bytes = lsfs_read(&src, buffer, sizeof(buffer))) > 0) { lsfs_write(&dst, buffer, bytes); } lsfs_close(&src); lsfs_close(&dst); ``` ### Q3: 如何判断路径是文件还是目录? **A:** 使用 `lsfs_stat()` 查询: ```c struct lsfs_dirent entry; if (lsfs_stat(path, &entry) == 0) { if (entry.type == LSFS_DIR_ENTRY_DIR) { printf("It's a directory\n"); } else { printf("It's a file\n"); } } ``` ### Q4: 如何检查磁盘剩余空间? **A:** 使用 `lsfs_statvfs()`: ```c struct lsfs_statvfs stat; if (lsfs_statvfs("/RAM:", &stat) == 0) { unsigned long free_bytes = stat.f_bfree * stat.f_frsize; printf("Free space: %lu bytes\n", free_bytes); } ``` ### Q5: 为什么POSIX API无法使用? **A:** 确保: 1. 在 `menuconfig` 中启用 `CONFIG_LVFS_POSIX_API_ALIAS` 2. 调用了 `lvfs_init()` 3. 文件系统已成功挂载