简单介绍了一下dfs代码框架和如何在rtthread平台上使用dfs分布式文件系统。工作比较忙先把目前整理的发出来,希望对小伙伴们有帮助,也希望玩过的朋友一起讨论指正。等空闲下来再继续细化深入分析一下。甚至看看能不能挂载个网络文件系统玩玩。
环境用的rtthreadstudio
rtthread版本:标准版4.0.3
硬件平台:自己做的产品电路板,mcu用的stm32f407vet6
dfs分布式文件系统框架如下:
主要特点:
支持多种类型的存储设备。
支持多种类型的文件系统,提供普通文件、设备文件、网络文件描述符的管理。
提供统一的posix文件操作接口:read、write、poll/select等。
dfs虚拟文件系统文件目录如下:
关键文件简单介绍如下(详见具体的api手册):
src文件夹: dfs虚拟文件系统层相关代码
dfs_posix.c 对上层的调用接口代码
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) 写入文件
off_t lseek(int fd, off_t offset, int whence) 移动文件读写位置
int rename(const char old_file, const char new_file) 重命名文件
int unlink(const char *pathname) 删除文件
int stat(const char file, struct stat buf) 读取文件信息
int fstat(int fildes, struct stat *buf) 读取文件状态
int fsync(int fildes) 把文件描述符对应文件的缓冲区数据,写入对应磁盘文件,清空缓存区
int fcntl(int fildes, int cmd, …) 文件的ioctl接口,针对dfs层只能读取和设置文件flags。对应具体文件系统,由具体文件系统的ioctl接口定义
int ioctl(int fildes, int cmd, …) 文件的ioctl接口,内部调用上面的fcntl,如此,对外只需调用ioctl即可
int ftruncate(int fd, off_t length) 按照length长度截断文件
int statfs(const char path, struct statfs buf) 读取文件系统信息,包括block大小,block总数,剩余block数,用于计算剩余空间
int mkdir(const char *path, mode_t mode) 创建文件夹,目前mode内部并没有使用,无实际意义。可配置为0x777
int rmdir(const char *pathname) 删除文件夹
dir opendir(const char name) 打开文件夹
struct dirent readdir(dir d) 读取文件夹,可打开一个文件夹后,连续调用此函数遍历读取文件夹内所有子文件夹和文件,dirent内的d_type表明文件类型,1为文件,2为文件夹
long telldir(dir *d) 读取目录流中的当前位置(尚未测试有何用途)
void seekdir(dir *d, off_t offset) 设置目录流中的读写位置(尚未测试有何用途)
void rewinddir(dir *d) 复位目录流中的读写位置(尚未测试有何用途)
int closedir(dir *d) 关闭文件夹
int chdir(const char *path) 更改当前目录
int access(const char *path, int amode) 测试文件是否可以访问
char getcwd(char buf, size_t size) 读取当前路径
dfs.c dfs文件系统初始化,文件句柄列表管理等接口函数
int dfs_init(void) 初始化dfs文件系统,如果使能devfs,则直接挂载。此接口程序默认被rtthread设置为组件前自动初始化,无需用户调用。需发生在挂载底层文件系统之前
void dfs_lock(void) 尝试获取dfs的互斥量,如果已被占用,则挂起当前线程,等待其它线程让出dfs操作权
void dfs_unlock(void) 释放dfs互斥量,让出dfs操作权
static int fd_alloc(struct dfs_fdtable *fdt, int startfd) 如果已打开的文件句柄列表内有空余句柄,则直接返回。如果没有空余,则在打开文件的时候需要新开辟空间,扩充已打开的文件句柄列表(每次扩充4个)。如果已超过同时打开文件的数量上限,则不再开辟。但这里没有直接返回错误,而是返回最大文件描述符。等待调用者做越界保护处理
int fd_new(void) 调用上面的fd_alloc函数,申请一个空文件描述符。如果越界(没有空闲设备描述符可用)则返回-1.否则返回+3的描述符.0,1,2的文件描述符用于承载终端设备文件描述符
struct dfs_fd *fd_get(int fd) 通过文件描述符,在文件句柄列表内找出对应的文件句柄
void fd_put(struct dfs_fd *fd) 如果文件句柄的ref_count减一后为0,则释放掉文件句柄。
int fd_is_open(const char *pathname) 查找某文件是否已打开,0为打开,-1为未打开。
const char dfs_subdir(const char directory, const char *filename) 在filename中去掉directory,返回相对子目录
char dfs_normalize_path(const char directory, const char *filename) 返回标准格式的路径
struct dfs_fdtable *dfs_fdtable_get(void) 返回文件句柄列表
int list_fd(void) 打印当前文件句柄列表内,已打开的文件属性
dfs_fs.c dfs层文件系统对底层具体文件系统的管理接口函数
int dfs_register(const struct dfs_filesystem_ops *ops) 注册一个具体的文件系统(dev,elm等)。具体操作为在文件系统操作符数组中,找出空闲句柄,并添加为新注册的操作符
struct dfs_filesystem dfs_filesystem_lookup(const char path) 根据路径名,在文件系统列表中查找对应的文件系统。路径名只要包含文件系统具体挂载点即可。比如给定一个文件的绝对路径名,即可找到它所在的文件系统
const char dfs_filesystem_get_mounted_path(struct rt_device device) 根据设备id,在文件系统列表中查找对应的文件系统,并返回对应文件系统的挂载点
int dfs_filesystem_get_partition(struct dfs_partition part,uint8_t buf,uint32_t pindex) 在指定存储空间内读取分区表。一般分区表在存储设备的第一个扇区。
int dfs_mount(const char device_name,const char path,const char filesystemtype,unsigned long rwflag,const void data) 根据设备名,把该设备挂载到指定路径。filesystemtype指定具体的文件系统类型,以使用具体的操作接口。rwflag为文件系统的读写属性,data为传的参数,是否有实际意义要看具体的文件系统操作接口。常用的elm和dev文件系统都无实际意义,直接给0即可。该函数需要等注册完具体的存储设备和dfs文件系统并在dfs文件系统内注册了具体的文件系统后,由用户调用,把具体的存储设备挂载到dfs的指定挂载点。并指定以哪种类型的文件系统操作接口处理此设备。在此之前无需open对应的存储设备,此函数内部会先打开存储设备再挂载
int dfs_unmount(const char *specialfile) 取消挂载,在文件系统列表中删除指定文件系统,关闭存储设备。specialfile只需包含文件系统挂载路径即可
int dfs_mkfs(const char fs_name, const char device_name) 根据fs_name指定的具体文件系统类型,把device_name指定的实际存储设备进行格式化,构建文件系统
int dfs_statfs(const char path, struct statfs buffer) 读取指定文件系统的信息,包括块大小,总块数以及剩余块数,可以用于查看设备剩余空间
void mkfs(const char fs_name, const char device_name) finsh函数命令的mkfs接口。使能了msh后,此函数未被使用
int df(const char *path) finsh函数命令的stafts接口,打印存储设备的容量和剩余空间信息。使能了msh后,此函数未被使用
dfs_file.c dfs文件系统层对于文件相关的处理接口函数,具体实现会分别调用对应实际文件系统的操作函数接口。既dfs面向下层文件系统的接口,移植具体文件系统的时候需要实现对接代码(用rtt移植的时候,rtt已经完成了此接口,在dfs_elm.c内)。
int dfs_file_open(struct dfs_fd fd, const char path, int flags) 按照flags属性打开path指定文件或文件夹,然后由fd文件句柄返回
int dfs_file_close(struct dfs_fd *fd) 关闭fd指定的文件或文件夹
int dfs_file_ioctl(struct dfs_fd fd, int cmd, void args) dfs层的ioctl内部接口,可读取和设置文件flags。并调用具体文件系统的ioctl接口实现底层具体功能
int dfs_file_read(struct dfs_fd fd, void buf, size_t len) dfs层的read接口,调用具体文件系统的read接口实现读取功能
int dfs_file_getdents(struct dfs_fd fd, struct dirent dirp, size_t nbytes) 根据文件操作符,读取dirent结构体对应的属性信息
int dfs_file_unlink(const char *path) dfs层删除接口,调用具体文件系统的unlink接口实现文件或文件夹的删除功能
int dfs_file_write(struct dfs_fd fd, const void buf, size_t len) dfs层的write接口,调用具体文件系统的write接口实现写入功能
int dfs_file_flush(struct dfs_fd *fd) dfs层的同步接口,调用具体文件系统的flush接口实现文件的同步,既把文件句柄内的缓存数据真正写入到存储设备
int dfs_file_lseek(struct dfs_fd *fd, off_t offset) dfs层的lseek接口,调用具体文件系统的lseek接口,实现对文件读取位置的移动
int dfs_file_stat(const char path, struct stat buf) dfs层的读取文件属性接口,也会调用具体文件系统的stat接口
int dfs_file_rename(const char oldpath, const char newpath) dfs层的rename接口,调用底层具体文件系统的rename接口,实现文件重命名
int dfs_file_ftruncate(struct dfs_fd *fd, off_t length) 调用具体文件系统的ioctl接口,实现对文件fd的截断(length字节)
void ls(const char *pathname) finsh的ls命令函数
void rm(const char *filename) finsh的rm命令函数
void cat(const char *filename) finsh的cat命令函数
static void copyfile(const char src, const char dst) dfs层实现的文件拷贝函数
static void copydir(const char src, const char dst) dfs层实现的文件夹拷贝函数,遍历子文件夹,把所有子文件拷贝到目标文件夹
static const char _get_path_lastname(const char path) 通过path路径,得到最后一级路径名
void copy(const char src, const char dst) finsh的copy命令函数,调用上面的copyfile和copydir实现
filesystems文件夹:dfs支持的具体文件系统,默认会有一个devfs文件夹,对应设备文件系统。开启了elm的fat文件系统模块后,会多一个elmfat文件夹,对应fatfs的代码
elmfat文件夹:fatfs对应的代码,其中ff.c为fatfs代码,dfs_elm.c为rtt实现的移植代码,承上启下,完成dfs的调用接口和与fatfs对接的接口,并通过具体设备(flash,sdcard)的操作接口实现具体的功能
dfs_elm.c 相对直接移植fatfs时diskio.c要实现的接口外,又多了一些和dfs层对接的接口。下面针对dfs_elm.c下的接口函数做简单说明
static int elm_result_to_dfs(fresult result) 做fatfs下的返回状态与dfs下的返回状态的转换
static int get_disk(rt_device_t id) 根据设备id,在挂载的磁盘列表中找到磁盘列表id
int dfs_elm_mount(struct dfs_filesystem fs, unsigned long rwflag, const void data) 承上启下,连接dfs层和fatfs内的mount接口,实现设备挂载,把设备id添加到挂载的磁盘列表内
int dfs_elm_unmount(struct dfs_filesystem *fs) 承上启下,连接dfs层和fatfs内的unmount接口,实现设备取消挂载,把设备id从磁盘列表中删除
int dfs_elm_mkfs(rt_device_t dev_id) 承上启下,连接dfs层和fatfs内的mkfs接口,实现存储设备的格式化
int dfs_elm_statfs(struct dfs_filesystem fs, struct statfs buf) 承上启下,连接dfs层和fatfs内的statfs和getfree接口,读取文件系统的存储空间信息
int dfs_elm_open(struct dfs_fd *file) 承上启下,连接dfs层和fatfs内的open或opendir接口,实现打开文件或文件夹功能
int dfs_elm_close(struct dfs_fd *file) 承上启下,连接dfs层和fatfs内的close接口,实现关闭文件或文件夹功能
int dfs_elm_ioctl(struct dfs_fd file, int cmd, void args) 承上启下,连接dfs层和fatfs内的ioctl接口,实现文件截断功能
int dfs_elm_read(struct dfs_fd file, void buf, size_t len) 承上启下,连接dfs层和fatfs内的read接口,实现文件读取功能
int dfs_elm_write(struct dfs_fd file, const void buf, size_t len) 承上启下,连接dfs层和fatfs内的write接口,实现文件写入功能
int dfs_elm_flush(struct dfs_fd *file) 承上启下,连接dfs层和fatfs内的flush和sync接口,实现文件的同步功能,把缓存数据写入磁盘
int dfs_elm_lseek(struct dfs_fd *file, rt_off_t offset) 承上启下,连接dfs层和fatfs内的lseek或seekdir接口,实现文件或文件夹的操作位置移动
int dfs_elm_getdents(struct dfs_fd file, struct dirent dirp, uint32_t count) 承上启下,调用fatfs的readdir接口,实现dfs层的getdents接口,读取文件dirent属性
int dfs_elm_unlink(struct dfs_filesystem fs, const char path) 承上启下,连接dfs层和fatfs内的funlink接口,实现文件的删除功能
int dfs_elm_rename(struct dfs_filesystem fs, const char oldpath, const char *newpath) 承上启下,连接dfs层和fatfs内的rename接口,实现改文件名的功能
int dfs_elm_stat(struct dfs_filesystem fs, const char path, struct stat *st) 承上启下,连接dfs层和fatfs内的stat接口,实现读取文件stat信息的功能
int elm_init(void) 调用dfs层的dfs_register接口,把“elm”的文件系统操作接口注册到dfs文件系统中。用于后面按照“elm”类型挂载实际的存储设备。此函数被rtt加入到了组件初始化列表,会自动运行,无需用户调用
dstatus disk_initialize(byte drv) 此函数为fatfs的初始化磁盘设备的接口,由于rtt实际初始化磁盘设备的代码也被加入到了自动初始化列表,所以此接口直接返回ok即可
dstatus disk_status(byte drv) 此函数为fatfs读取磁盘设备状态的接口。rtt的移植,并没有对此函数做具体实现,直接返回的ok
dresult disk_read(byte drv, byte *buff, dword sector, uint count) 此函数为fatfs读取磁盘扇区数据的接口,对接具体的设备驱动读取接口
dresult disk_write(byte drv, const byte *buff, dword sector, uint count) 此函数为fatfs写入磁盘扇区数据的接口,对接具体的设备驱动写入接口
dresult disk_ioctl(byte drv, byte ctrl, void *buff) 此函数为fatfs的ioctl接口,对接具体设备驱动的control接口
dword get_fattime(void) 此函数为fatfs获取系统时间的接口,用于记录文件和文件夹的创建和修改时间
int ff_cre_syncobj(byte drv, ff_sync_t *m) 如开启文件重入功能,则需要实现重入保护相关代码,rtt利用互斥量实现,此接口创建互斥量。ff_sync_t到rt_mutex_t的重定义在ffconf.h中定义
int ff_del_syncobj(ff_sync_t m) fatfs的删除互斥量的接口
int ff_req_grant(ff_sync_t m) fatfs的获取互斥量的接口
void ff_rel_grant(ff_sync_t m) fatfs的释放互斥量的接口
void *ff_memalloc(uint size) 当定义ff_use_lfn == 3的时候,长文件名需要在堆上面开辟空间,此时需要实现fatfs开辟存储空间的接口,对应rt_malloc接口
void ff_memfree(void *mem) fatfs释放存储空间的接口,对应rt_free接口
dfs以fatfs系统挂载sdcard相关代码调用时序图:
大致按照从底层到顶层的初始化过程:
sdio硬件外设初始化(自动):
mmcsd设备初始化(自动):
fatfs文件系统初始化(自动):
dfs文件系统初始化(自动):
sdcard检测线程,检测是否有sdcard或mmccard插入到sd卡槽并进行卡识别:
sdcard挂载到dfs系统(用户调用):
具体使用过程
如上所述,其实在rtthread平台上使用dfs文件系统挂载sd卡已经很简单了,大致分一下几步:
1、用cubemx生成sdio的最底层外设初始化代码hal_sd_mspinit(),复制到board.c
sdio配置如下:
sdio的dma配置如下:
sdio的nvic配置如下:
生成代码如下(复制到board.c):
1
2
3sd_handletypedef hsd;
4
5dma_handletypedef hdma_sdio_rx;
6
7dma_handletypedef hdma_sdio_tx;
8
9/**
10
11* @brief sd msp initialization
12
13* this function configures the hardware resources used in this example
14
15* @param hsd: sd handle pointer
16
17* @retval none
18
19*/
20
21void hal_sd_mspinit(sd_handletypedef* hsd)
22
23{
24
25 gpio_inittypedef gpio_initstruct = {0};
26
27 if(hsd->instance==sdio)
28
29 {
30
31 /* user code begin sdio_mspinit 0 */
32
33 /* user code end sdio_mspinit 0 */
34
35 /* peripheral clock enable */
36
37 __hal_rcc_sdio_clk_enable();
38
39 __hal_rcc_gpioc_clk_enable();
40
41 __hal_rcc_gpiod_clk_enable();
42
43 /**sdio gpio configuration
44
45 pc8 ------> sdio_d0
46
47 pc9 ------> sdio_d1
48
49 pc10 ------> sdio_d2
50
51 pc11 ------> sdio_d3
52
53 pc12 ------> sdio_ck
54
55 pd2 ------> sdio_cmd
56
57 */
58
59 gpio_initstruct.pin = gpio_pin_8|gpio_pin_9|gpio_pin_10|gpio_pin_11
60
61 |gpio_pin_12;
62
63 gpio_initstruct.mode = gpio_mode_af_pp;
64
65 gpio_initstruct.pull = gpio_nopull;
66
67 gpio_initstruct.speed = gpio_speed_freq_very_high;
68
69 gpio_initstruct.alternate = gpio_af12_sdio;
70
71 hal_gpio_init(gpioc, &gpio_initstruct);
72
73 gpio_initstruct.pin = gpio_pin_2;
74
75 gpio_initstruct.mode = gpio_mode_af_pp;
76
77 gpio_initstruct.pull = gpio_nopull;
78
79 gpio_initstruct.speed = gpio_speed_freq_very_high;
80
81 gpio_initstruct.alternate = gpio_af12_sdio;
82
83 hal_gpio_init(gpiod, &gpio_initstruct);
84
85 /* sdio dma init */
86
87 /* sdio_rx init */
88
89 hdma_sdio_rx.instance = dma2_stream3;
90
91 hdma_sdio_rx.init.channel = dma_channel_4;
92
93 hdma_sdio_rx.init.direction = dma_periph_to_memory;
94
95 hdma_sdio_rx.init.periphinc = dma_pinc_disable;
96
97 hdma_sdio_rx.init.meminc = dma_minc_enable;
98
99 hdma_sdio_rx.init.periphdataalignment = dma_pdataalign_word;
100
101 hdma_sdio_rx.init.memdataalignment = dma_mdataalign_word;
102
103 hdma_sdio_rx.init.mode = dma_pfctrl;
104
105 hdma_sdio_rx.init.priority = dma_priority_low;
106
107 hdma_sdio_rx.init.fifomode = dma_fifomode_enable;
108
109 hdma_sdio_rx.init.fifothreshold = dma_fifo_threshold_full;
110
111 hdma_sdio_rx.init.memburst = dma_mburst_inc4;
112
113 hdma_sdio_rx.init.periphburst = dma_pburst_inc4;
114
115 if (hal_dma_init(&hdma_sdio_rx) != hal_ok)
116
117 {
118
119 error_handler();
120
121 }
122
123 __hal_linkdma(hsd,hdmarx,hdma_sdio_rx);
124
125 /* sdio_tx init */
126
127 hdma_sdio_tx.instance = dma2_stream6;
128
129 hdma_sdio_tx.init.channel = dma_channel_4;
130
131 hdma_sdio_tx.init.direction = dma_memory_to_periph;
132
133 hdma_sdio_tx.init.periphinc = dma_pinc_disable;
134
135 hdma_sdio_tx.init.meminc = dma_minc_enable;
136
137 hdma_sdio_tx.init.periphdataalignment = dma_pdataalign_word;
138
139 hdma_sdio_tx.init.memdataalignment = dma_mdataalign_word;
140
141 hdma_sdio_tx.init.mode = dma_pfctrl;
142
143 hdma_sdio_tx.init.priority = dma_priority_low;
144
145 hdma_sdio_tx.init.fifomode = dma_fifomode_enable;
146
147 hdma_sdio_tx.init.fifothreshold = dma_fifo_threshold_full;
148
149 hdma_sdio_tx.init.memburst = dma_mburst_inc4;
150
151 hdma_sdio_tx.init.periphburst = dma_pburst_inc4;
152
153 if (hal_dma_init(&hdma_sdio_tx) != hal_ok)
154
155 {
156
157 error_handler();
158
159 }
160
161 __hal_linkdma(hsd,hdmatx,hdma_sdio_tx);
162
163 /* sdio interrupt init */
164
165 hal_nvic_setpriority(sdio_irqn, 6, 0);
166
167 hal_nvic_enableirq(sdio_irqn);
168
169 /* user code begin sdio_mspinit 1 */
170
171 /* user code end sdio_mspinit 1 */
172
173 }
174
175}
2、用rtthreadstudio配置dfs组件和fatfs组件:
studio组件配置如下,如只需移植fatfs挂载sd卡,则只需开启“dfs”,“fatfs”和“sdio”即可,最大扇区也可以用默认的512,我的是同时挂载了spi接口的串行flash,flash的扇区是4096,所以修改了一下这个参数:
3、挂载设备到文件系统:
如上所述,在rtthread平台上使用dfs,只要配置好组件(代码里面就是宏定义),基本所有的初始化代码都会自动调用,无需用户干涉。这里用户只需要找到sd卡设备,然后挂载即可。
1 #define sdcardpath /sdcard0
2
3 void sdmnt(void)
4
5{
6
7 int sta;
8
9 if(rt_device_find(sd0) != rt_null)
10
11 {
12
13 sta = dfs_mount(sd0, sdcardpath, elm, 0, 0);
14
15 if(sta == rt_eok)
16
17 {
18
19 log_i(sd card mount to sdcardpath);
20
21 }
22
23 else {
24
25 log_w(sd card mount to sdcardpath failed!);
26
27 }
28
29 }
30
31}
32
33msh_cmd_export(sdmnt, mount the sd card to local filesystem);
4、挂载后就可以按照dfs_posix.c里面的通用接口进行调用使用了。当然,dfs已经实现了很多基于命令行的操作命令,比如“cd”,“ls”,“cat”等。使用方法基本和linux下差不太多,但功能上确实还是没那么强大,这也是很正常的,如果也做那么强大,岂不是又变成了和linux一个量级的系统了,失去了轻量级可抢占系统的意义了。
————————————————
版权声明:本文为rt-thread论坛用户「吉利咕噜2022」的原创文章,遵循cc 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:
https://club.rt-thread.org/ask/article/ca44a91d66b137c9.html
你可以添加微信17775983565为好友,注明:公司+姓名,拉进rt-thread官方微信交流群!
爱我就给我点在看
点击阅读原文进入官网
原文标题:基于rtthread的dfs文件系统组件使用笔记
文章出处:【微信公众号:rtthread物联网操作系统】欢迎添加关注!文章转载请注明出处。
USB PD有望结束快充市场的混战局面
iPhone7航空货运单曝光 iPhone7有可能会面临严重缺货
输入信号耦合的放大电路工作原理分析
储能变流器EMC测试经验分享
新纳传感推出基于MEMS技术的OpenIMU330测量单元
基于RTThread的DFS文件系统组件使用笔记
Maxim面向HVAC和楼宇自动化推出CAN收发器
海洋光学新型NeoFox Sport手持式光学氧传感器
风力发电防雷监测浪涌保护器的应用解决方案
工业网关开启“数智”供热新时代
华为P30 曝光,水滴+后置三摄
使用UWB进行设计重新构想短距离无线应用
比特币的核心缺陷可能是在于公共账簿
Agilent54645A乐价出售54645A示波器54645A
常用的电流互感器接线图分享
基于MachXO设计的PLD控制开发技术
高通能否对Intel霸主地位构成威胁?
科幻正成为现实,脑机接口离我们还有多远
抗渗水性测试仪的原理及功能说明
口袋物联来科普-eSIM与实体物联卡的区别?