DFS组件介绍
DFS 是 RT-Thread 提供的虚拟文件系统组件,全称为 Device File System,即设备虚拟文件系统,文件系统的名称使用类似 UNIX 文件、文件夹的风格。
目录结构示例
类unix风格
功能
-
为应用程序提供统一的 POSIX 文件和目录操作接口:read、write、poll/select 等。
-
支持多种类型的文件系统,如 FatFS、RomFS、DevFS 等,并提供普通文件、设备文件、网络文件描述符的管理。
-
支持多种类型的存储设备,如 SD Card、SPI Flash、Nand Flash 等。
架构
POSIX Interface Layer: 给应用提供POSIX标准的接口。
Virtual File System Layer: 各个不同的文件系统
Device Abstraction Layer: 将物理设备如 SD Card、SPI Flash、Nand Flash,抽象成符合文件系统能够访问的设备,例如 FAT 文件系统要求存储设备必须是块设备类型。
配置选项
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qUDNlmpi-1667636296220)(581553fe9755868f90d8ee50a95cfb7a.png)]
初始化
-
初始化 DFS 组件
int dfs_init(void); // INIT_PREV_EXPORT(dfs_init);
-
初始化具体类型的文件系统
int dfs_register(const struct dfs_filesystem_ops *ops);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CzZVwMVr-1667636296221)(fc03fdeb6e3ddd8a62851100d0e7fd20.png)]
e.g.: elem-FAT file system init func will call dfs_register()
int elm_init(void); // INIT_COMPONENT_EXPORT(elm_init);
-
在存储器上创建块设备。(块设备,就是指数据读写是按块进行的。 相对应的字符设备,其数据读写是按字节进行的。)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1WbxAscZ-1667636296222)(f3ec0279462586c111ac24d09ed6dce4.png)]
e.g. : SPI flash 抽象为块设备并注册
static int rt_hw_spiflash_init(void); // INIT_COMPONENT_EXPORT(rt_hw_spiflash_init);
-
格式化块设备。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ML4lZjfc-1667636296222)(70f6f748e7b1a9682f2891f51d43a074.png)]
e.g.: elm-FAT 文件系统格式化SPI flash对应块设备
也就是手动格式化,要么写在应用的代码中,要么mkfs + 设备名 + 文件系统类型 格式化,默认的文件系统是elm-FAT, 需要先使用list_device 查找设备的名称。
// rt_device_find(char* name) // 通过名称找到对应的device
-
挂载块设备到 DFS 目录中。
int dfs_mount(const char *device_name, const char *path, const char *filesystemtype, unsigned long rwflag, const void *data);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FQ8HJegc-1667636296223)(d685677a169b8e58766a3adfba358eb2.png)]
参数 参数 device_name 已经格式化的块设备名称 path 挂载路径,即挂载点 filesystemtype 挂载的文件系统类型 rwflag 读写标志位 data 特定文件系统的私有数据 如果只有一个存储设备,则可以直接挂载到根目录 / 上。
可以手动挂载也可以自动挂载,通过宏RT_USING_DFS_MNTTABLE进行控制。
注:使用自动挂载的时候,需要提供自动挂载表。
-
当文件系统不再使用,可以将它卸载。
int dfs_unmount(const char *specialfile);
代码阅读
DFS文件结构
├─ dfs
│ ├─ filesystems // rt-thread支持的文件系统(TODO:可以自己添加文件系统吗?)
│ │ ├─ devfs
│ │ │ ├─ devfs.c
│ │ │ ├─ devfs.h
│ │ │ └─ SConscript
│ │ ├─ elmfat // 文件系统elmfat
│ │ │ ├─ dfs_elm.c
│ │ │ ├─ diskio.h
│ │ │ ├─ ff.c
│ │ │ ├─ ff.h
│ │ │ ├─ ffconf.h
│ │ │ ├─ ffunicode.c
│ │ │ └─ SConscript
│ │ ├─ ramfs // 文件系统ramfs
│ │ │ ├─ dfs_ramfs.c
│ │ │ ├─ dfs_ramfs.h
│ │ │ └─ SConscript
│ │ ├─ romfs
│ │ │ ├─ dfs_romfs.c
│ │ │ ├─ dfs_romfs.h
│ │ │ ├─ romfs.c
│ │ │ └─ SConscript
│ │ └─ SConscript
│ ├─ include
│ │ ├─ dfs.h
│ │ ├─ dfs_file.h
│ │ ├─ dfs_fs.h
│ │ └─ dfs_private.h
│ ├─ Kconfig
│ ├─ SConscript
│ └─ src
│ ├─ dfs.c // init and other fd op
│ ├─ dfs_file.c // DFS file op
│ ├─ dfs_fs.c // DFS file system op
│ └─ dfs_posix.c // posix API to app
文件管理
int open(const char *file, int flags, ...); // 打开
int close(int fd); // 关闭
int read(int fd, void *buf, size_t len); // 读取
int write(int fd, const void *buf, size_t len); // 写入
int rename(const char *old, const char *new); // 重命名
int stat(const char *file, struct stat *buf); // 获取状态
int unlink(const char *pathname); // 删除文件
int fsync(int fildes); // 同步内存中所有已修改的文件数据到储存设备(什么作用?)
int statfs(const char *path, struct statfs *buf); // 查询文件系统相关信息(都有哪些信息?)
IO状态监测
int select( int nfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout); // TODO: how to use?
目录管理
int mkdir(const char *path, mode_t mode); // 创建(mode 似乎未启用)
int rmdir(const char *pathname); // 删除
DIR* opendir(const char* name); // 打开
int closedir(DIR* d); // 关闭
struct dirent* readdir(DIR *d); // 读取
long telldir(DIR *d); // 获取位置
void seekdir(DIR *d, off_t offset); // 设置下次读取目录
void rewinddir(DIR *d); // 重设
当前工作路径和相对路径的使用需要在DFS配置中手动设置。
File Descriptor 管理
DFS 维护了一个fdTable,动态分配内存,最大由DFS_FD_MAX决定。
int fd_new(void); // 新建fd
struct dfs_fd *fd_get(int fd); // 获取index对应的fd,同时referCount++
void fd_put(struct dfs_fd *fd); // referCount--
int fd_is_open(const char *pathname); // 在fdTable中查pathname
// fd context
struct dfs_fd
{
uint16_t magic; /* file descriptor magic number */
uint16_t type; /* Type (regular or socket) */
char *path; /* Name (below mount point) */
int ref_count; /* Descriptor reference count */
struct dfs_filesystem *fs;
const struct dfs_file_ops *fops;
uint32_t flags; /* Descriptor flags */
size_t size; /* Size in bytes */
off_t pos; /* Current file position */
void *data; /* Specific file system data */
};
File System 管理
DFS 维护了两个结构体数组
const struct dfs_filesystem_ops *.[DFS_FILESYSTEM_TYPES_MAX];
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);
};
和 struct dfs_filesystem filesystem_table[DFS_FILESYSTEMS_MAX];
struct dfs_filesystem
{
rt_device_t dev_id; /* Attached device */
char *path; /* File system mount point */
const struct dfs_filesystem_ops *ops; /* Operations for file system type */
void *data; /* Specific file system data */
};
FS相关操作
int dfs_register(const struct dfs_filesystem_ops *ops); // FS 注册到DFS中
struct dfs_filesystem *dfs_filesystem_lookup(const char *path); // 查找path关联的FS
const char *dfs_filesystem_get_mounted_path(struct rt_device *device); // 查找device挂载的path
int dfs_filesystem_get_partition(struct dfs_partition *part,
uint8_t *buf,
uint32_t pindex); // TODO
int dfs_mount(const char *device_name,
const char *path,
const char *filesystemtype,
unsigned long rwflag,
const void *data); // 挂载fs到指定path,并放进FS数组中
int dfs_unmount(const char *specialfile); // 去挂载
int dfs_mkfs(const char *fs_name, const char *device_name); // 将device格式化为FS
int dfs_statfs(const char *path, struct statfs *buffer); // 查内存
块设备的注册
I/O 设备管理层实现了对设备驱动程序的封装。应用程序通过图中的"I/O设备管理层"提供的标准接口访问底层设备,设备驱动程序的升级、更替不会对上层应用产生影响。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qPWglZHk-1667636296226)(28170096049a1a25a9ec40cbfe09234b.png)]
rt_err_t rt_device_register(rt_device_t dev,
const char *name,
rt_uint16_t flags);
// rt_device_t
struct rt_device
{
struct rt_object parent; /**< inherit from rt_object */
enum rt_device_class_type type; /**< device type */
rt_uint16_t flag; /**< device flag */
rt_uint16_t open_flag; /**< device open flag */
rt_uint8_t ref_count; /**< reference count */
rt_uint8_t device_id; /**< 0 - 255 */
/* device call back */
rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);
#ifdef RT_USING_DEVICE_OPS
const struct rt_device_ops *ops;
#else
/* common device interface */
rt_err_t (*init) (rt_device_t dev);
rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag);
rt_err_t (*close) (rt_device_t dev);
rt_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
rt_size_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
rt_err_t (*control)(rt_device_t dev, int cmd, void *args);
#endif /* RT_USING_DEVICE_OPS */
#ifdef RT_USING_POSIX_DEVIO
const struct dfs_file_ops *fops;
struct rt_wqueue wait_queue;
#endif /* RT_USING_POSIX_DEVIO */
void *user_data; /**< device private data */
};
RT_USING_DEVICE_OPS有什么作用?
https://club.rt-thread.org/ask/question/de46c2b599b4a6c2.html
what is SFUD?
是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险.gitHub:https://github.com/armink/SFUD
为什么devfs_init()是在dfs_init()中调用,而其它文件系统的初始化是交给rt_compoents_init()完成?
TODO: FATFS,DEVFS 源码分析
文件系统是和I/O设备管理器交互,硬件驱动调用rt_device_register()在I/O设备管理器中注册为块设备,文件系统通过块设备提供的接口进行相关的操作。
elm-FATFS介绍
├─ elmfat
│ ├─ dfs_elm.c
│ ├─ diskio.h
│ ├─ ff.c
│ ├─ ff.h
│ ├─ ffconf.h
│ ├─ ffunicode.c
│ └─ SConscript
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6xRXiiq4-1667636296226)(d48e527e0a8d45aad06d7642301c68e6.png)]
FATFS移植步骤
-
数据类型:在integer.h 里面去定义好数据的类型。这里需要了解你用的编译器的数据类型,并根据编译器定义好数据类型。
-
配置:通过ffconf.h配置FATFS的相关功能,以满足你的需要。
-
函数编写:打开diskio.c,进行底层驱动编写,一般需要编写6 个接口函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Xq3NTqU-1667636296227)(d61f60db9cc1f03aee79831a562a8ffc.png)]
rt-thread实现放在了dfs_elm.c当中;