rt-thread中的dfs组件源码分析
先研究一下 dfs_init() 它初始化一些数据结构(dfs的成员变量或成员函数),这些结构体是对各个文件系统的一个抽象,是具体文件系统的公共部分。
rt-thread的dfs是基于面向对象的思想编写的,它的UML大致如下图所示:
1. 先研究一下 dfs_init() 它初始化一些数据结构(dfs的成员变量或成员函数),这些结构体是对各个文件系统的一个抽象,是具体文件系统的公共部分。
const struct dfs_filesystem_ops *filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX];
struct dfs_filesystem filesystem_table[DFS_FILESYSTEMS_MAX];
static struct dfs_fdtable _fdtab;
其中,DFS_FILESYSTEM_TYPES_MAX和DFS_FILESYSTEMS_MAX是文件系统的最多类型、以及文件系统最大个数
2. 之后就是具体的文件系统初始化,并调用 dfs_register() 注册进dfs,而实际注册就是用具体的文件系统填充和实现抽象的数据结构(dfs的成员变量或成员函数)
const struct dfs_filesystem_ops *filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX];
struct dfs_filesystem filesystem_table[DFS_FILESYSTEMS_MAX];
3. 所以下面重点分析 dfs_filesystem_ops,它的结构如下:
Dfs_fs.h (components\dfs\include)
/* File system operations */
struct dfs_filesystem_ops
{
char *name;
uint32_t flags; /* flags for file system operations */
/* operations for file */
const struct dfs_file_ops *fops;
/* mount and unmount file system */
int (*mount) (struct dfs_filesystem *fs, unsigned long rwflag, const void *data);
int (*unmount) (struct dfs_filesystem *fs);
/* make a file system */
int (*mkfs) (rt_device_t devid);
int (*statfs) (struct dfs_filesystem *fs, struct statfs *buf);
int (*unlink) (struct dfs_filesystem *fs, const char *pathname);
int (*stat) (struct dfs_filesystem *fs, const char *filename, struct stat *buf);
int (*rename) (struct dfs_filesystem *fs, const char *oldpath, const char *newpath);
};
而它中间又嵌套了一个 const struct dfs_file_ops *fops;
Dfs_file.h (components\dfs\include)
struct dfs_file_ops
{
int (*open) (struct dfs_fd *fd);
int (*close) (struct dfs_fd *fd);
int (*ioctl) (struct dfs_fd *fd, int cmd, void *args);
int (*read) (struct dfs_fd *fd, void *buf, size_t count);
int (*write) (struct dfs_fd *fd, const void *buf, size_t count);
int (*flush) (struct dfs_fd *fd);
int (*lseek) (struct dfs_fd *fd, off_t offset);
int (*getdents) (struct dfs_fd *fd, struct dirent *dirp, uint32_t count);
int (*poll) (struct dfs_fd *fd, struct rt_pollreq *req);
};
4. 以yaffs2文件系统为例
dfs_yaffs_init
dfs_register(&_fsops) -> 填充 dfs_filesystem_ops
而 _fsops在 Dfs_yaffs_s.c (packages\yaffs2-latest)中
static const struct dfs_filesystem_ops _fsops =
{
"yaffs",
DFS_FS_FLAG_DEFAULT,
&_fops,
dfs_yaffs_mount,
dfs_yaffs_unmount,
dfs_yaffs_mkfs,
dfs_yaffs_statfs,
dfs_yaffs_unlink,
dfs_yaffs_stat,
dfs_yaffs_rename,
};
它是yaffs实现了 dfs 的相关接口
5. 下一步我们进一步分析 yaffs 中如何实现 dfs接口,我们以比较简单的 dfs_yaffs_mkfs 为例
其实它调用了 mtd 代码,即用 mtd 来实现dfs的接口
由于 dfs_yaffs_mkfs 比较薄, 调用mtd只是中转了一下,提取出了 yaffs_dev 而已,之后就直接调用 yaffs_format_reldev
Dfs_yaffs_s.c
dfs_yaffs_mkfs
yaffs_format_reldev
6. 在dfs_yaffs_s.c 关于对dfs接口实现过程中,有一个 dfs_filesystem 类(在dfs_init()中也提及过)与dfs有什么关系?它是如何关联到 dfs 的?
答:通过 dfs_mount() 这个函数建立了 dfs_filesystem 与 dfs_filesystem_ops 之间的联系,即 在 dfs_mount 中会将 rt_device、dfs_filesystem_ops、以及path(mount点)关联起来
它是更上层的结构体,它包含了更多的信息
后面 dfs_filesystem_ops 中大多数成员函数都是通过 dfs_filesystem 进行传参的
7. 回过头来,我们分析一下 dfs_filesystem_ops 中的 dfs_yaffs_mount()
它的最重要的参数是 struct dfs_filesystem *fs, 它具有 rt_device_t dev_id; 而这个 dev_id 是 rt_mtd_t, 所以可以关联到 struct yaffs_dev
对于yaffs来说,dfs_filesystem_ops中的大多数函数指针都有现成的实现,其实并没有依赖于mtd技术
8. 真正依赖于mtd的是yaffs的底层本身(涉及到操作硬件),而不是dfs。所以dfs与yaffs之间的对接相对比较薄,即通过 dfs_filesystem_ops 的若干接口和 dfs_file_ops 即可
9. 下一步研究一下 dfs 关于文件的操作 dfs_file_ops
static const struct dfs_file_ops _fops =
{
dfs_yfile_open,
dfs_yfile_close,
dfs_yfile_ioctl,
dfs_yfile_read,
dfs_yfile_write,
dfs_yfile_flush,
dfs_yfile_lseek,
dfs_yfile_getdents,
RT_NULL, /* poll interface */
};
这些函数的接口有一个很重要的类,struct dfs_fd,关于它在 dfs_init() 中有涉及到
memset(&_fdtab, 0, sizeof(_fdtab));
10. 下面重点分析一下 struct dfs_fd
首先有一个全局的 static struct dfs_fdtable _fdtab; 所有文件对应一个fd,它们都链接于这个静态的 _fdtab, 为了封装它设置了一个get方法:struct dfs_fdtable* dfs_fdtable_get(void)
涉及到 fd 的函数 都在 dfs.c 中,有 fd_alloc(), fd_new(), fd_get(), fd_put(), fd_is_open() 等
fd_new
idx = fd_alloc(fdt, 0);
其中 fd_alloc() 中会递增的申请fd,直到超过 DFS_FD_MAX, 系统一开始的时候是没有fd的,根据需要开始申请fd(每次申请4个, cnt = fdt->maxfd + 4;)目前申请最大的fd数存放在 fdt->maxfd 中也提及过)与dfs有什么关系?
当文件关闭时,这些申请的fd并不会释放,只是空出来,下次如果有人需要申请时直接返回即可
这里需要注意的是 fd_get(), 它是根据一个int的fd转换成struct dfs_fd *d
11. 很显然,每次打开一个文件都会有一个fd进行对应,那么这个fd是如何对应到文件中的呢?
由于在dfs之上还有一层dfs_posix, 在每次调用 open() 时,
open
fd_new -> 返回的是 一个fd 的下标(实际上是“下标”+DFS_FD_OFFSET),结果是int型的
fd_get -> 将下标(减回DFS_FD_OFFSET)转换成 struct dfs_fd *d
dfs_file_open
dfs_normalize_path