DFS笔记

DFS组件介绍

DFS 是 RT-Thread 提供的虚拟文件系统组件,全称为 Device File System,即设备虚拟文件系统,文件系统的名称使用类似 UNIX 文件、文件夹的风格。

目录结构示例

类unix风格

请添加图片描述

功能

  1. 为应用程序提供统一的 POSIX 文件和目录操作接口:read、write、poll/select 等。

  2. 支持多种类型的文件系统,如 FatFS、RomFS、DevFS 等,并提供普通文件、设备文件、网络文件描述符的管理。

  3. 支持多种类型的存储设备,如 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)]

初始化

  1. 初始化 DFS 组件

    int dfs_init(void);     // INIT_PREV_EXPORT(dfs_init); 
    
  2. 初始化具体类型的文件系统

    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);
    
  3. 在存储器上创建块设备。(块设备,就是指数据读写是按块进行的。 相对应的字符设备,其数据读写是按字节进行的。)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1WbxAscZ-1667636296222)(f3ec0279462586c111ac24d09ed6dce4.png)]

    e.g. : SPI flash 抽象为块设备并注册

    static int rt_hw_spiflash_init(void);     // INIT_COMPONENT_EXPORT(rt_hw_spiflash_init);
    
  4. 格式化块设备。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ML4lZjfc-1667636296222)(70f6f748e7b1a9682f2891f51d43a074.png)]

    e.g.: elm-FAT 文件系统格式化SPI flash对应块设备

    也就是手动格式化,要么写在应用的代码中,要么mkfs + 设备名 + 文件系统类型 格式化,默认的文件系统是elm-FAT, 需要先使用list_device 查找设备的名称。

    // rt_device_find(char* name)   // 通过名称找到对应的device
    
  5. 挂载块设备到 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进行控制。

    注:使用自动挂载的时候,需要提供自动挂载表。

  6. 当文件系统不再使用,可以将它卸载。

    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移植步骤

  1. 数据类型:在integer.h 里面去定义好数据的类型。这里需要了解你用的编译器的数据类型,并根据编译器定义好数据类型。

  2. 配置:通过ffconf.h配置FATFS的相关功能,以满足你的需要。

  3. 函数编写:打开diskio.c,进行底层驱动编写,一般需要编写6 个接口函数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Xq3NTqU-1667636296227)(d61f60db9cc1f03aee79831a562a8ffc.png)]

rt-thread实现放在了dfs_elm.c当中;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值