概述
本文档主要介绍 Vela 操作系统中文件操作(open、read、write、close)的主要流程。
文件系统整体架构
Vela 系统采用 VFS(Virtual File System) 架构,主要包含:
VFS 层 – 提供统一的文件操作接口
具体文件系统实现(FAT、PROCFS、TMPFS等)
设备驱动层
文件系统相关的重要数据结构:
struct inode – 文件系统节点,代表文件/目录
struct file – 打开文件实例
struct file_operations – 文件操作方法集
struct mountpt_operations – 挂载点操作方法集
open() 调用流程
应用层调用 open()
进入系统调用 nx_open()
调用 inode_search() 查找路径对应的 inode
如果是普通文件:
调用对应文件系统的 open 操作(如 fat_open)
分配 file 结构体,关联 inode
如果是设备文件:
调用设备驱动的 open 操作
关联驱动的 file_operations
返回文件描述符
open() 详细调用流程
1. 系统调用入口
应用程序调用open()后,进入系统调用层:
int open(const char *path, int oflags, ...)
-> int nx_open(const char *path, int oflags, mode_t mode)
-> int file_open(const char *path, int oflags, mode_t mode)
-> int file_vopen(struct file *filep, const char *path, int oflags, mode_t mode)
2. 路径解析与inode查找
在file_vopen()中,首先需要解析路径找到对应的inode:
调用inode_search()开始查找:
SETUP_SEARCH(&desc, path, false); // 初始化查找描述符
ret = inode_find(&desc); // 开始查找inode
inode_find()内部调用_inode_search():
从根节点(g_root_inode)开始搜索
按路径组件逐级遍历inode树
支持软链接和挂载点的处理
查找过程中的关键步骤:
比较当前节点名与路径组件
处理特殊情况(软链接、挂载点)
返回找到的inode及相关信息
inode_search返回结果包含:
node: 找到的目标inode
peer: 同级前一个节点
parent: 父节点
relpath: 挂载点内的相对路径
3. 文件打开处理
根据找到的inode类型执行不同的打开操作:
设备驱动文件:
if (INODE_IS_DRIVER(inode)) {
// 调用驱动的open方法
ret = inode->u.i_ops->open(filep);
}
块设备文件:
if (INODE_IS_BLOCK(inode)) {
// 创建字符设备代理
ret = block_proxy(filep, path, oflags);
}
挂载点文件:
if (INODE_IS_MOUNTPT(inode)) {
// 调用文件系统的open方法
ret = inode->u.i_mops->open(filep, relpath, oflags, mode);
}
4. 文件结构设置
为打开的文件分配file结构:
memset(filep, 0, sizeof(*filep));
filep->f_oflags = oflags; // 设置打开标志
filep->f_inode = inode; // 关联inode
根据oflags检查访问权限:
ret = inode_checkflags(inode, oflags);
设置文件操作方法:
普通文件: 使用文件系统提供的file_operations
设备文件: 使用驱动提供的file_operations
目录: 使用特殊的目录操作方法
5. 错误处理
在open()过程中的主要错误情况:
路径解析错误:
ENOENT: 路径不存在
ENOTDIR: 路径组件不是目录
ELOOP: 符号链接循环
权限检查错误:
EACCES: 无访问权限
EROFS: 只读文件系统写入
资源分配错误:
ENOMEM: 内存不足
EMFILE: 打开文件过多
6. 返回结果
成功打开:
返回有效的文件描述符
file结构完成初始化
inode引用计数增加
打开失败:
返回负的错误码
清理已分配资源
释放inode引用
read() 调用流程
应用层调用 read()
进入系统调用 nx_read()
根据文件描述符获取 file 结构体
如果是普通文件:
调用对应文件系统的 read 方法读取数据
更新文件位置指针
如果是设备文件:
调用设备驱动的 read 操作读取数据
返回读取的字节数
write() 调用流程
应用层调用 write()
进入系统调用 nx_write()
根据文件描述符获取 file 结构体
如果是普通文件:
调用对应文件系统的 write 方法写入数据
更新文件位置指针和大小信息
如果是设备文件:
调用设备驱动的 write 操作写入数据
返回写入的字节数
close() 调用流程
应用层调用 close()
进入系统调用 nx_close()
根据文件描述符获取 file 结构体
如果是普通文件:
调用对应文件系统的 close 方法
释放 file 结构体
如果是设备文件:
调用设备驱动的 close 操作
释放相关资源
返回操作结果
关键数据结构
1. struct inode – 文件系统节点
inode 是文件系统最基本的数据结构,代表文件系统中的一个节点(文件、目录等):
struct inode {
FAR struct inode *i_parent; /* 父节点链接 */
FAR struct inode *i_peer; /* 同级节点链接 */
FAR struct inode *i_child; /* 子节点链接 */
atomic_short i_crefs; /* 引用计数 */
uint16_t i_flags; /* 节点标志 */
union inode_ops_u u; /* 节点操作方法 */
ino_t i_ino; /* inode序号 */
FAR void *i_private; /* 私有数据 */
char i_name[1]; /* 节点名称 */
}
2. struct file_operations – 文件操作方法集
定义了文件的基本操作方法:
struct file_operations {
int (*open)(FAR struct file *filep);
int (*close)(FAR struct file *filep);
ssize_t (*read)(FAR struct file *filep, FAR char *buffer, size_t buflen);
ssize_t (*write)(FAR struct file *filep, FAR const char *buffer, size_t buflen);
off_t (*seek)(FAR struct file *filep, off_t offset, int whence);
int (*ioctl)(FAR struct file *filep, int cmd, unsigned long arg);
int (*mmap)(FAR struct file *filep, FAR struct mm_map_entry_s *map);
// ...等其他操作
};
3. struct mountpt_operations – 挂载点操作方法集
定义了文件系统挂载点的操作方法:
struct mountpt_operations {
// 打开文件
int (*open)(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode);
// 基本文件操作
int (*close)(FAR struct file *filep);
ssize_t (*read)(FAR struct file *filep, FAR char *buffer, size_t buflen);
ssize_t (*write)(FAR struct file *filep, FAR const char *buffer, size_t buflen);
// 目录操作
int (*opendir)(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct fs_dirent_s **dir);
int (*closedir)(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir);
int (*readdir)(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir,
FAR struct dirent *entry);
// 文件系统操作
int (*bind)(FAR struct inode *blkdriver, FAR const void *data,
FAR void **handle);
int (*unbind)(FAR void *handle, FAR struct inode **blkdriver,
unsigned int flags);
// ...等其他操作
};
4. struct fs_dirent_s – 目录项结构
表示打开的目录:
struct fs_dirent_s {
FAR struct inode *fd_root; /* 目录对应的inode节点 */
FAR char *fd_path; /* 目录路径 */
};
5. union inode_ops_u – inode操作union
将不同类型的操作方法统一到一个union中:
union inode_ops_u {
FAR const struct file_operations *i_ops; /* 驱动操作方法 */
FAR const struct block_operations *i_bops; /* 块设备操作方法 */
FAR const struct mountpt_operations *i_mops; /* 挂载点操作方法 */
};
这些数据结构协同工作,构成了完整的VFS层次结构:
inode 构成树状结构,表示文件系统的层次关系
file_operations/mountpt_operations 提供实际的操作方法
fs_dirent_s 管理目录遍历
inode_ops_u 统一了不同类型节点的操作接口
重要的宏定义
用于判断inode类型:
#define INODE_IS_PSEUDODIR(i) // 伪目录
#define INODE_IS_DRIVER(i) // 设备驱动
#define INODE_IS_BLOCK(i) // 块设备
#define INODE_IS_MOUNTPT(i) // 挂载点
#define INODE_IS_NAMEDSEM(i) // 命名信号量
#define INODE_IS_SOCKET(i) // Socket
#define INODE_IS_PIPE(i) // 管道
用于设置inode类型:
#define INODE_SET_DRIVER(i) // 设置为驱动
#define INODE_SET_BLOCK(i) // 设置为块设备
#define INODE_SET_MOUNTPT(i) // 设置为挂载点
#define INODE_SET_NAMEDSEM(i) // 设置为命名信号量
重要注意事项
所有文件系统操作都需要获取信号量保护
设备文件操作需要考虑并发访问
文件系统操作可能会失败,需要正确处理错误
注意资源释放和引用计数管理
主要代码实现
核心代码主要在以下位置:
fs/inode/ – inode 管理相关代码
fs/vfs/ – 虚拟文件系统层代码
fs/driver/ – 设备驱动接口代码
fs/fat/ – FAT文件系统实现
fs/procfs/ – procfs 文件系统实现
6. struct file – 打开文件实例
struct file 结构体代表一个已打开的文件实例,是系统中最基础的文件操作数据结构之一。每个打开的文件都对应一个 struct file 实例。
struct file {
int f_oflags; /* 打开模式标志位 */
#ifdef CONFIG_FS_REFCOUNT
int f_refs; /* 引用计数 */
#endif
off_t f_pos; /* 文件当前读写位置 */
FAR struct inode *f_inode; /* 关联的inode节点 */
FAR void *f_priv; /* 私有数据指针 */
#ifdef CONFIG_FDSAN
uint64_t f_tag_fdsan; /* 文件描述符检查标签,初始为0 */
#endif
#ifdef CONFIG_FDCHECK
uint8_t f_tag_fdcheck; /* 文件描述符检查标签,初始为0 */
#endif
#if CONFIG_FS_BACKTRACE > 0
FAR void *f_backtrace[CONFIG_FS_BACKTRACE]; /* 文件打开时的栈回溯信息 */
#endif
#if CONFIG_FS_LOCK_BUCKET_SIZE > 0
bool locked; /* 文件锁状态: false - 未锁, true - 已锁 */
#endif
};
主要字段说明:
f_oflags – 打开标志
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR: 读写打开
O_APPEND: 追加写入
O_CREAT: 如不存在则创建
O_EXCL: 如存在则失败
O_TRUNC: 打开时清空文件
f_refs – 引用计数
记录有多少个文件描述符指向这个文件实例
当计数为0时,这个文件实例可以被释放
f_pos – 文件位置
记录当前读写位置相对于文件开头的偏移量
seek() 操作会修改这个值
read()/write() 操作会根据这个值确定读写位置
f_inode – inode节点
指向这个文件对应的inode节点
通过inode可以找到具体的文件操作方法
文件系统相关的元数据都保存在inode中
f_priv – 私有数据
用于保存驱动程序的私有数据
对于设备文件,通常保存设备特定的状态信息
对于普通文件,可能保存文件系统特定的缓冲区等
文件描述符检查
f_tag_fdsan: 64位检查标签
f_tag_fdcheck: 8位检查标签
用于检测文件描述符泄露和非法使用
调试支持
f_backtrace[ ]: 保存打开文件时的调用栈
便于调试文件资源泄露问题
文件锁
locked: 记录文件是否被加锁
用于实现文件级别的并发控制
使用示例:
// 打开文件
struct file file;
int ret = file_open(&file, "/tmp/test.txt", O_RDWR|O_CREAT);
// 读写文件
char buf[100];
ssize_t n = file_read(&file, buf, sizeof(buf));
file_write(&file, "hello", 5);
// 修改位置
file_seek(&file, 0, SEEK_SET); // 移到开头
file_seek(&file, 0, SEEK_END); // 移到末尾
// 关闭文件
file_close(&file);
重要说明:
struct file 实例在打开文件时创建,关闭文件时释放
一个文件可以被多次打开,每次都会创建新的 struct file 实例
父子进程会共享文件表中的 struct file 实例
文件描述符本质是进程文件表中的索引,指向对应的 struct file 实例
标准输入(0)、标准输出(1)、标准错误(2)都对应各自的 struct file 实例


















暂无评论内容