vlambda博客
学习文章列表

rt-thread中的dfs组件源码分析

  1. 先研究一下 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_MAXDFS_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