簡單介紹了一下DFS代碼框架和如何在RTThread平臺上使用DFS分布式文件系統。工作比較忙先把目前整理的發出來,希望對小伙伴們有幫助,也希望玩過的朋友一起討論指正。等空閑下來再繼續細化深入分析一下。甚至看看能不能掛載個網絡文件系統玩玩。
DFS分布式文件系統框架如下:
主要特點:
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_HandleTypeDefhsd;
4
5DMA_HandleTypeDefhdma_sdio_rx;
6
7DMA_HandleTypeDefhdma_sdio_tx;
8
9/**
10
11*@briefSDMSPInitialization
12
13*Thisfunctionconfiguresthehardwareresourcesusedinthisexample
14
15*@paramhsd:SDhandlepointer
16
17*@retvalNone
18
19*/
20
21voidHAL_SD_MspInit(SD_HandleTypeDef*hsd)
22
23{
24
25GPIO_InitTypeDefGPIO_InitStruct={0};
26
27if(hsd->Instance==SDIO)
28
29{
30
31/*USERCODEBEGINSDIO_MspInit0*/
32
33/*USERCODEENDSDIO_MspInit0*/
34
35/*Peripheralclockenable*/
36
37__HAL_RCC_SDIO_CLK_ENABLE();
38
39__HAL_RCC_GPIOC_CLK_ENABLE();
40
41__HAL_RCC_GPIOD_CLK_ENABLE();
42
43/**SDIOGPIOConfiguration
44
45PC8------>SDIO_D0
46
47PC9------>SDIO_D1
48
49PC10------>SDIO_D2
50
51PC11------>SDIO_D3
52
53PC12------>SDIO_CK
54
55PD2------>SDIO_CMD
56
57*/
58
59GPIO_InitStruct.Pin=GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
60
61|GPIO_PIN_12;
62
63GPIO_InitStruct.Mode=GPIO_MODE_AF_PP;
64
65GPIO_InitStruct.Pull=GPIO_NOPULL;
66
67GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_VERY_HIGH;
68
69GPIO_InitStruct.Alternate=GPIO_AF12_SDIO;
70
71HAL_GPIO_Init(GPIOC,&GPIO_InitStruct);
72
73GPIO_InitStruct.Pin=GPIO_PIN_2;
74
75GPIO_InitStruct.Mode=GPIO_MODE_AF_PP;
76
77GPIO_InitStruct.Pull=GPIO_NOPULL;
78
79GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_VERY_HIGH;
80
81GPIO_InitStruct.Alternate=GPIO_AF12_SDIO;
82
83HAL_GPIO_Init(GPIOD,&GPIO_InitStruct);
84
85/*SDIODMAInit*/
86
87/*SDIO_RXInit*/
88
89hdma_sdio_rx.Instance=DMA2_Stream3;
90
91hdma_sdio_rx.Init.Channel=DMA_CHANNEL_4;
92
93hdma_sdio_rx.Init.Direction=DMA_PERIPH_TO_MEMORY;
94
95hdma_sdio_rx.Init.PeriphInc=DMA_PINC_DISABLE;
96
97hdma_sdio_rx.Init.MemInc=DMA_MINC_ENABLE;
98
99hdma_sdio_rx.Init.PeriphDataAlignment=DMA_PDATAALIGN_WORD;
100
101hdma_sdio_rx.Init.MemDataAlignment=DMA_MDATAALIGN_WORD;
102
103hdma_sdio_rx.Init.Mode=DMA_PFCTRL;
104
105hdma_sdio_rx.Init.Priority=DMA_PRIORITY_LOW;
106
107hdma_sdio_rx.Init.FIFOMode=DMA_FIFOMODE_ENABLE;
108
109hdma_sdio_rx.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_FULL;
110
111hdma_sdio_rx.Init.MemBurst=DMA_MBURST_INC4;
112
113hdma_sdio_rx.Init.PeriphBurst=DMA_PBURST_INC4;
114
115if(HAL_DMA_Init(&hdma_sdio_rx)!=HAL_OK)
116
117{
118
119Error_Handler();
120
121}
122
123__HAL_LINKDMA(hsd,hdmarx,hdma_sdio_rx);
124
125/*SDIO_TXInit*/
126
127hdma_sdio_tx.Instance=DMA2_Stream6;
128
129hdma_sdio_tx.Init.Channel=DMA_CHANNEL_4;
130
131hdma_sdio_tx.Init.Direction=DMA_MEMORY_TO_PERIPH;
132
133hdma_sdio_tx.Init.PeriphInc=DMA_PINC_DISABLE;
134
135hdma_sdio_tx.Init.MemInc=DMA_MINC_ENABLE;
136
137hdma_sdio_tx.Init.PeriphDataAlignment=DMA_PDATAALIGN_WORD;
138
139hdma_sdio_tx.Init.MemDataAlignment=DMA_MDATAALIGN_WORD;
140
141hdma_sdio_tx.Init.Mode=DMA_PFCTRL;
142
143hdma_sdio_tx.Init.Priority=DMA_PRIORITY_LOW;
144
145hdma_sdio_tx.Init.FIFOMode=DMA_FIFOMODE_ENABLE;
146
147hdma_sdio_tx.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_FULL;
148
149hdma_sdio_tx.Init.MemBurst=DMA_MBURST_INC4;
150
151hdma_sdio_tx.Init.PeriphBurst=DMA_PBURST_INC4;
152
153if(HAL_DMA_Init(&hdma_sdio_tx)!=HAL_OK)
154
155{
156
157Error_Handler();
158
159}
160
161__HAL_LINKDMA(hsd,hdmatx,hdma_sdio_tx);
162
163/*SDIOinterruptInit*/
164
165HAL_NVIC_SetPriority(SDIO_IRQn,6,0);
166
167HAL_NVIC_EnableIRQ(SDIO_IRQn);
168
169/*USERCODEBEGINSDIO_MspInit1*/
170
171/*USERCODEENDSDIO_MspInit1*/
172
173}
174
175}
2、用RTThreadStudio配置DFS組件和Fatfs組件:
Studio組件配置如下,如只需移植Fatfs掛載SD卡,則只需開啟“DFS”,“Fatfs”和“SDIO”即可,最大扇區也可以用默認的512,我的是同時掛載了SPI接口的串行Flash,Flash的扇區是4096,所以修改了一下這個參數:
3、掛載設備到文件系統:
如上所述,在RTThread平臺上使用DFS,只要配置好組件(代碼里面就是宏定義),基本所有的初始化代碼都會自動調用,無需用戶干涉。這里用戶只需要找到SD卡設備,然后掛載即可。
1#defineSDCardPath"/sdcard0"
2
3voidsdmnt(void)
4
5{
6
7intsta;
8
9if(rt_device_find("sd0")!=RT_NULL)
10
11{
12
13sta=dfs_mount("sd0",SDCardPath,"elm",0,0);
14
15if(sta==RT_EOK)
16
17{
18
19LOG_I("sdcardmounttoSDCardPath");
20
21}
22
23else{
24
25LOG_W("sdcardmounttoSDCardPathfailed!");
26
27}
28
29}
30
31}
32
33MSH_CMD_EXPORT(sdmnt,mountthesdcardtolocalfilesystem);
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官方微信交流群!
愛我就給我點在看
點擊閱讀原文進入官網
-
RT-Thread
+關注
關注
31文章
1273瀏覽量
39933
原文標題:基于RTThread的DFS文件系統組件使用筆記
文章出處:【微信號:RTThread,微信公眾號:RTThread物聯網操作系統】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論