7.1 文件系统
7.1 文件系统
📚 本节导读
学习时长: 约 60 分钟
难度级别: ⭐⭐⭐⭐☆
前置知识: C 语言文件操作、POSIX 文件 API、第 5 章设备驱动框架
🎯 学习目标
- 理解 OneOS VFS(虚拟文件系统)的设计理念与分层架构
- 掌握 vfs_file、vfs_files_ops、vfs_mountpoint 核心结构体
- 掌握 vfs_open / vfs_close / vfs_read / vfs_write 等 POSIX 文件 API
- 了解 DevFS 设备文件系统及其将设备映射文件的设计
- 了解 LittleFS、FatFs、JFFS2、YAFFS、NFS 等文件系统类型
- 掌握 Kconfig 配置项(挂载点、文件描述符上限、自动挂载等)
- 能够使用 VFS 进行文件读写操作
一、概述
OneOS 采用 VFS(Virtual File System,虚拟文件系统) 架构,上层应用提供统一的 POSIX 标准文件操作接口,同时支持多种底层文件系统实现。VFS 的核心设计思想是抽象与解耦:上层应用无需底层是 Flash 文件系统、FAT 文件系统还是网络文件系统,统一使用 open / read / write / close 等标准 API 进行文件操作。
VFS 的源码位于 components/fs/ 目录,核心头文件 vfs.h。
核心设计特点:
- POSIX 兼容:提供标准的 open / close / read / write / lseek / stat / opendir / readdir 等接口
- 多文件系统支持:LittleFS(掉电保护)、FatFs(FAT/exFAT)、JFFS2(日志 Flash)、YAFFS(NAND Flash)、NFS(网络)、DevFS(设备文件)
- 挂载点机制:每个文件系统挂载到独立的目录路径,支持多分区挂载
- 设备文件系统(DevFS):将设备抽象文件节点,通过文件 API 操作设备
- 自动挂载:支持启动时根据配置文件自动挂载文件系统
- IO 多路复用:支持 select/poll 机制
二、核心数据结构
2.1 vfs_file —— 文件描述符结构体
每个打开的文件都对应一个 vfs_file 结构体(源码 vfs.h 第52-64行):
struct vfs_file
{
unsigned short type; /* 类型(普通文件或目录) */
int flags; /* 打开模式标志 */
const char *path; /* 文件路径(挂载点下的相对路径) */
off_t off_ptr; /* 当前读写位置偏移 */
struct vfs_mountpoint *mnt_point; /* 所属挂载点 */
void *desc; /* 底层文件系统的文件描述符 */
#ifdef OS_USING_VFS_DEVFS
void *private; /* DevFS 私有数据(设备注册时使用) */
#endif
};| 字段 | 类型 | 说明 |
|---|---|---|
type | unsigned short | 文件类型:FT_REGULAR(1) 普通文件、FT_DIRECTORY(2) 目录、FT_DEVICE(3) 设备文件 |
flags | int | 打开模式(如 O_RDONLY、O_WRONLY、O_RDWR、O_CREAT 等) |
path | const char * | 挂载点下的相对路径 |
off_ptr | off_t | 当前文件读写位置,支持 lseek 修改 |
mnt_point | struct vfs_mountpoint * | 指向所属挂载点的指针 |
desc | void * | 底层文件系统描述符,实现文件系统间的隔离 |
2.2 vfs_files_ops —— 文件系统操作接口
每种文件系统类型需要实现 vfs_files_ops 中定义的操作函数集(源码 vfs.h 第72-105行):
struct vfs_files_ops
{
char *fs_name; /* 文件系统名称 */
/* 文件操作 */
int (*open) (struct vfs_file *fp);
int (*close) (struct vfs_file *fp);
int (*read) (struct vfs_file *fp, void *buf, size_t count);
int (*write) (struct vfs_file *fp, const void *buf, size_t count);
int (*lseek) (struct vfs_file *fp, off_t offset, int whence);
int (*sync) (struct vfs_file *fp);
int (*ioctl) (struct vfs_file *fp, unsigned long cmd, void *args);
#ifdef OS_USING_IO_MULTIPLEXING
int (*poll) (struct vfs_file *fp, struct vfs_pollfd *poll_fd, os_bool_t poll_setup);
#endif
/* 目录操作 */
int (*opendir) (struct vfs_dir *dp);
int (*closedir) (struct vfs_dir *dp);
int (*readdir) (struct vfs_dir *dp, struct dirent *dentry);
int (*seekdir) (struct vfs_dir *dp, off_t offset);
long (*telldir) (struct vfs_dir *dp);
/* 文件/目录通用操作 */
int (*rename) (struct vfs_mountpoint *mnt_point, const char *oldpath, const char *newpath);
int (*unlink) (struct vfs_mountpoint *mnt_point, const char *path);
int (*stat) (struct vfs_mountpoint *mnt_point, const char *path, struct stat *buf);
/* 文件系统操作 */
int (*mkfs) (void *dev);
int (*statfs) (struct vfs_mountpoint *mnt_point, struct statfs *buf);
int (*mount) (struct vfs_mountpoint *mnt_point, unsigned long mountflag, const void *data);
int (*unmount) (struct vfs_mountpoint *mnt_point);
};该结构体将文件系统操作分为四个层次:
- 文件操作:open / close / read / write / lseek / sync / ioctl / poll
- 目录操作:opendir / closedir / readdir / seekdir / telldir
- 通用操作:rename / unlink / stat
- 文件系统操作:mkfs / statfs / mount / unmount
2.3 vfs_mountpoint —— 挂载点结构体
每个已挂载的文件系统对应一个挂载点(源码 vfs.h 第107-115行):
struct vfs_mountpoint
{
unsigned short is_inited; /* 是否已初始化 */
unsigned short ref_cnt; /* 引用计数 */
void *dev; /* 关联的设备指针 */
char *mnt_path; /* 挂载路径 */
const struct vfs_files_ops *ops; /* 文件系统操作接口 */
void *fs_instance; /* 文件系统实例(如 LittleFS 的 lfs_t) */
};2.4 vfs_mountinfo —— 自动挂载配置结构体
用于启动时自动挂载文件系统(源码 vfs.h 第117-125行):
struct vfs_mountinfo
{
char *mnt_path; /* 挂载点路径 */
char *fal_part; /* FAL 分区名称 */
char *fs_type; /* 文件系统类型:littlefs / fat ... */
uint8_t auto_format; /* 挂载失败时是否自动格式化 */
char reserved[7];
};三、文件类型
VFS 定义了四种文件类型(源码 vfs.h 第45-50行):
| 宏 | 值 | 含义 |
|---|---|---|
FT_INVALID | 0 | 无效类型 |
FT_REGULAR | 1 | 普通文件 |
FT_DIRECTORY | 2 | 目录 |
FT_DEVICE | 3 | 设备文件(DevFS) |
四、核心 API
4.1 文件操作
VFS 提供标准的 POSIX 文件操作接口:
int open(const char *path, int flags, ...); /* 打开/创建文件 */
int close(int fd); /* 关闭文件 */
int read(int fd, void *buf, size_t count); /* 读取文件 */
int write(int fd, const void *buf, size_t count); /* 写入文件 */
off_t lseek(int fd, off_t offset, int whence); /* 移动文件读写位置 */
int fsync(int fd); /* 同步文件到存储 */
int stat(const char *path, struct stat *buf); /* 获取文件状态 */
int unlink(const char *path); /* 删除文件 */
int rename(const char *oldpath, const char *newpath); /* 重命名文件 */4.2 目录操作
DIR *opendir(const char *path); /* 打开目录 */
int closedir(DIR *dp); /* 关闭目录 */
struct dirent *readdir(DIR *dp); /* 读取目录项 */
int mkdir(const char *path, mode_t mode); /* 创建目录 */
int rmdir(const char *path); /* 删除目录 */4.3 文件系统操作
int mount(const char *dev_name, const char *path, const char *fs_type,
unsigned long mountflag, const void *data); /* 挂载文件系统 */
int unmount(const char *path); /* 卸载文件系统 */
int mkfs(const char *dev_name, const char *fs_type); /* 格式化文件系统 */
int statfs(const char *path, struct statfs *buf); /* 获取文件系统信息 */4.4 fd_to_fp —— 文件描述符转换
struct vfs_file *fd_to_fp(int fd);将文件描述符(整数)转换为 VFS 文件结构体指针,用于内部实现。该函数声明于 vfs.h 第126行。
五、DevFS 设备文件系统
DevFS 是 OneOS 特有的设备文件系统,将设备抽象为文件节点,使开发者可以通过文件 API 操作设备(对应 Kconfig 中 OS_USING_VFS_DEVFS 配置项)。
启用 DevFS 后,设备文件通常挂载在 /dev/ 目录下:
// 例如:通过文件 API 操作串口设备
int fd = open("/dev/uart0", O_RDWR);
write(fd, "Hello", 5);
char buf[64];
read(fd, buf, sizeof(buf));
close(fd);六、支持的文件系统类型
6.1 LittleFS
- 配置项:
OS_USING_VFS_LFS - 特点:专为微控制器设计,支持掉电保护(power-loss resilience),磨损均衡
- 适用场景:SPI NOR Flash、内部 Flash 等需要数据安全性的场景
- 关键配置:
LFS_NAME_MAX、LFS_FILE_MAX、LFS_LOOKAHEAD_SIZE、LFS_THREADSAFE
6.2 FatFs
- 配置项:
OS_USING_VFS_FATFS - 特点:通用 FAT/exFAT 文件系统,兼容性好,支持长文件名
- 适用场景:SD 卡、U 盘等需要与 PC 交换数据的场景
- 关键配置:
OS_VFS_FAT_CODE_PAGE、OS_VFS_FAT_USE_LFN、OS_VFS_FAT_MAX_LFN、OS_VFS_FAT_DR
6.3 JFFS2
- 配置项:
OS_USING_VFS_JFFS2 - 特点:日志型 Flash 文件系统 v2,支持磨损均衡和掉电保护
- 适用场景:NOR Flash 大容量存储
6.4 YAFFS
- 配置项:
OS_USING_VFS_YAFFS - 特点:专为 NAND Flash 设计的文件系统
- 适用场景:NAND Flash 存储设备
6.5 NFS
- 配置项:
OS_USING_VFS_NFS - 特点:网络文件系统 v3 客户端,可挂载远程 NFS 服务器
- 适用场景:需要访问远程文件服务器的场景
- 关键配置:
OS_NFS_HOST_EXPORT(NFS 服务器地址和导出路径)
6.6 CuteFS
- 配置项:
OS_USING_VFS_CUTEFS - 特点:轻量级内存文件系统,运行在 RAM Disk 上
- 适用场景:临时文件存储、测试
七、Kconfig 配置详解
VFS 配置位于 components/fs/Kconfig:
7.1 基础配置
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
OS_USING_VFS | bool | y | 启用虚拟文件系统 |
VFS_MOUNTPOINT_MAX | int | 4 | 最大挂载点数量 |
VFS_FILESYSTEM_TYPES_MAX | int | 4 | 最大文件系统类型数量 |
VFS_FD_MAX | int | 16 | 最大打开文件数 |
7.2 自动挂载
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
OS_USING_AUTO_MOUNT | bool | n | 启用自动挂载(需 fs_cfg.c 描述挂载信息) |
7.3 文件系统类型
| 配置项 | 支持的文件系统 |
|---|---|
OS_USING_VFS_DEVFS | 设备文件系统 |
OS_USING_VFS_CUTEFS | CuteFS 内存文件系统 |
OS_USING_VFS_JFFS2 | JFFS2 日志 Flash 文件系统 |
OS_USING_VFS_YAFFS | YAFFS NAND Flash 文件系统 |
OS_USING_VFS_FATFS | FatFs FAT/exFAT 文件系统 |
OS_USING_VFS_NFS | NFS v3 网络文件系统 |
OS_USING_VFS_LFS | LittleFS 文件系统 |
八、使用示例
8.1 基本文件操作
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
void basic_file_ops(void)
{
int fd;
char buf[128];
struct stat st;
/* 打开文件(创建并写入) */
fd = open("/data/test.txt", O_CREAT | O_WRONLY, 0666);
if (fd < 0) {
os_kprintf("Failed to open file\r\n");
return;
}
/* 写入数据 */
write(fd, "Hello OneOS!\r\n", 14);
close(fd);
/* 读取文件 */
fd = open("/data/test.txt", O_RDONLY);
read(fd, buf, sizeof(buf));
os_kprintf("Read: %s\r\n", buf);
close(fd);
/* 获取文件状态 */
stat("/data/test.txt", &st);
os_kprintf("File size: %ld bytes\r\n", st.st_size);
}8.2 目录遍历
#include <dirent.h>
void list_directory(const char *path)
{
DIR *dp;
struct dirent *entry;
dp = opendir(path);
if (dp == NULL) {
os_kprintf("Failed to open directory: %s\r\n", path);
return;
}
os_kprintf("Contents of %s:\r\n", path);
while ((entry = readdir(dp)) != NULL) {
os_kprintf(" %s (%s)\r\n", entry->d_name,
entry->d_type == DT_DIR ? "DIR" : "FILE");
}
closedir(dp);
}8.3 文件系统挂载
#include <vfs.h>
void mount_filesystems(void)
{
/* 格式化 SPI Flash 分区为 LittleFS */
mkfs("spi_flash0", "littlefs");
/* 挂载 LittleFS 到 /data 目录 */
if (mount("spi_flash0", "/data", "littlefs", 0, NULL) != 0) {
os_kprintf("Mount failed!\r\n");
return;
}
os_kprintf("LittleFS mounted at /data\r\n");
/* 挂载 SD 卡为 FatFs */
if (mount("sd0", "/sdcard", "fat", 0, NULL) != 0) {
os_kprintf("SD card mount failed!\r\n");
return;
}
os_kprintf("FatFs mounted at /sdcard\r\n");
}8.4 自动挂载配置
/* 在 fs_cfg.c 中定义自动挂载表 */
#include <vfs.h>
const struct vfs_mountinfo g_fs_mount_table[] = {
{
.mnt_path = "/data",
.fal_part = "files",
.fs_type = "littlefs",
.auto_format = 1,
},
{
.mnt_path = "/sdcard",
.fal_part = "sd0",
.fs_type = "fat",
.auto_format = 0,
},
};📝 本节小结
本节详细介绍了 OneOS VFS 虚拟文件系统。VFS 通过 vfs_files_ops 操作接口抽象底层文件系统,通过 vfs_mountpoint 挂载点机制支持多文件系统共存。核心数据结构 vfs_file 封装了文件描述符、读写位置和底层文件系统引用。支持 LittleFS、FatFs、JFFS2、YAFFS、NFS、DevFS、CuteFS 七种文件系统类型,提供标准 POSIX 文件操作 API。Kconfig 中提供灵活的配置项,支持挂载点数量、文件描述符上限、自动挂载等。