RT-Thread学习之虚拟文件系统(DFS)

  • Post author:
  • Post category:其他




文件系统简介

早期的嵌入式系统由于需要存储的数据比较少,所以一般是直接在存储设备中指定地址写入数据的方法来存储数据。

随着嵌入式设备功能的发展,存储的数据越来越多,此时就需要一种新的数据管理方式——文件系统。

  • 文件系统是一套实现了数据的存储、分级组织、访问和获取等操作的抽象数据类型 (Abstract data type),是一种用于向用户提供底层数据访问的机制。
  • 文件系统通常存储的基本单位是文件,即数据是按照一个个文件的方式进行组织。
  • 当文件比较多时,将导致文件繁多,不易分类、重名的问题。而文件夹作为一个容纳多个文件的容器而存在。



DFS介绍

DFS是 RT-Thread 提供的虚拟文件系统组件,全称为 Device File System,即设备虚拟文件系统,文件系统的名称使用类似 UNIX 文件、文件夹的风格,目录结构如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Irjumohy-1595147628635)(https://www.rt-thread.org/document/site/programming-manual/filesystem/figures/fs-dir.png)]

在RT-Thread DFS中,文件系统有统一的根目录,使用/来表示,目录的分隔符號是/,这与UNIX/Linux完全相同,与windows不同。



DFS架构

RT-Thread DFS组件的主要功能特点有:

  • 为应用程序提供统一的 POSIX 文件和目录操作接口:read、write、poll/select 等。
  • 支持多种类型的文件系统,如

    FatFS、RomFS、DevFS

    等,并提供普通文件、设备文件、网络文件描述符的管理。
  • 支持多种类型的存储设备,如 SD Card、SPI Flash、Nand Flash 等。

DFS的层次架构如下图所示,主要分为POSIX接口层、虚拟文件系统层和设备抽象层。



POSIX接口层

  • POSIX 表示可移植操作系统接口(Portable Operating System Interface of UNIX,缩写 POSIX)
  • POSIX标准定义了操作系统应该为应用程序提供的接口标准
  • 是IEEE为要在各种 UNIX操作系统上运行的软件而定义的一系列API标准的总称。
  • 使用poll/select接口可以阻塞地同时探测一组支持非阻塞的I/O设备是否有事情发生,直到某一个设备触发了事件或者超过了指定的等待时间。



虚拟文件系统层

用户可以将具体的文件系统注册到 DFS 中,如 FatFS、RomFS、DevFS 等,下面介绍几种常用的文件系统类型:

  • FatFS:专为小型嵌入式设备开发的兼容微软FAT格式的文件系统,具有良好的硬件无关性以及可移植性,是RT-Thread中最常用的文件系统类型;
  • RomFS:是一种简单的、紧凑的、只读的文件系统,不支持动态擦写保存,按顺序存放数据,因而支持应用程序以XIP方式运行,在系统运行时节省RAM空间;
  • Jffs2:一种日志闪存文件系统。主要用于 NOR 型闪存,基于 MTD 驱动层,可读写的、支持数据压缩的、基于哈希表的日志型文件系统,并提供了崩溃 / 掉电安全保护,提供写平衡支持等。
  • DevFS:即设备文件系统,开启该功能后,可以将系统中的设备在 /dev 文件夹下虚拟成文件,使得设备可以按照文件的操作方式使用 read、write 等接口进行操作。
  • NFS 网络文件系统:一项在不同机器、不同操作系统之间通过网络共享文件的技术。
  • UFFS:Ultra-low-cost Flash File System(超低功耗的闪存文件系统)的简称,国人开发的、专为嵌入式设备等小内存环境中使用 Nand Flash 的开源文件系统。



挂载管理

文件系统的初始化过程一般分为以下几个步骤:

  1. 初始化DFS组件
  2. 初始化具体类型的文件系统
  3. 在存储器上创建块设备
  4. 格式化块设备
  5. 挂载块设备到DFS目录中
  6. 当文件系统不再使用,可以将它卸载



初始化DFS组件

DFS 组件的的初始化是由 dfs_init() 函数完成。



注册文件系统

int dfs_register(const struct dfs_filesystem_ops *ops);

该函数不需要用户调用,他会被不同文件系统的初始化函数调用,如 elm-FAT 文件系统的初始化函数elm_init()。开启对应的文件系统后,如果开启了自动初始化(默认开启),文件系统初始化函数也将被自动调用。注册过程如下图所示:



将存储设备注册为块设备

因为只有块设备才可以挂载到文件系统上,因此需要在存储设备上创建所需的块设备。如果存储设备是 SPI Flash,则可以使用 “串行 Flash 通用驱动库 SFUD” 组件,它提供了各种 SPI Flash 的驱动,并将 SPI Flash 抽象成块设备用于挂载,注册块设备过程如下图所示:



格式化文件系统

注册了块设备之后,还需要在块设备上创建指定类型的文件系统,也就是格式化文件系统。可以使用 dfs_mkfs() 函数对指定的存储设备进行格式化,创建文件系统,格式化文件系统的接口如下所示:

int dfs_mkfs(const char * fs_name, const char * device_name);

以 elm-FAT 文件系统格式化块设备为例,格式化过程如下图所示:



挂载文件系统

在RT-Thread中,挂载是指将一个存储设备挂接到一个已存在的路径上。我们要访问存储设备中的文件,必须将文件所在的分区挂载到一个已存在的路径上,然后通过这个路径来访问存储设备。挂载文件系统的接口如下所示:

int dfs_mount(const char   *device_name,
              const char   *path,
              const char   *filesystemtype,
              unsigned long rwflag,
              const void   *data);

如果只有一个存储设备,则可以直接挂载到根目录/上。



卸载文件系统

当某个文件系统不需要再使用了,就可以将它卸载掉,接口函数如下所示:

int dfs_unmount(const char *specialfile);



文件管理

对文件的操作一般都要基于文件描述符fd,如下图所示:



打开和关闭文件

打开或创建一个文件可以调用下面的open()函数:

int open(const char *file, int flags, ...);

flags: 指定打开文件的方式,O_RDONLY\O_WRONLY…

int close(int fd);



读写数据

int read(int fd, void *buf, size_t len);

向文件中写入数据可使用write()函数:

int write(int fd, const void *buf, size_t len);



取得状态

获取文件状态可使用下面的stat()函数:

int stat(const char *file, struct stat *buf);



删除文件

删除指定目录下的文件可使用unlink()函数:

int unlink(const char *pathname);



同步文件数据到存储设备

同步内存中所有已修改的文件数据到储存设备可使用fsync()函数:

int fsync(int fildes);



查询文件系统相关信息

int statfs(const char *path, struct statfs *buf);



监视I/O设备状态

int select( int nfds,
            fd_set *readfds,
            fd_set *writefds,
            fd_set *exceptfds,
            struct timeval *timeout);



目录管理



DFS配置选项

文件系统在 menuconfig 中具体配置路径如下:

RT-Thread Components  --->
    Device virtual file system  --->



elm-FatFs文件配置选项

在menuconfig中开启elem-FatFs文件系统后可对elm-FatFs做进一步配置,配置菜单描述及对应的宏定义可以参考相关源码。



更多配置

FatFs 本身支持非常多的配置选项,配置非常灵活。下面文件为 FatFs 的配置文件,可以修改这个文件来定制 FatFs。

components/dfs/filesystems/elmfat/ffconf.h



参考资料


虚拟文件系统



版权声明:本文为XMJYever原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。