1.1 文件系統引入
在早期的嵌入式系統中,需要存儲的數據比較少,數據類型也比較單一,往往使用直接在存儲設備中的指定地址寫入數據的方法來存儲數據。然而隨著嵌入式設備功能的發展,需要存儲的數據越來越多,也越來越復雜,這時仍使用舊方法來存儲并管理數據就變得非常繁瑣困難。因此我們需要新的數據管理方式來簡化存儲數據的組織形式,這就是文件系統的由來。
1.2 虛擬文件系統引入
為了統一眾多不同類型的文件系統,虛擬文件系統對實際文件系統進行抽象,采用統一的文件系統向用戶提供相應的一組統一的標準的文件操作接口(open,read,close,select,poll等)。
2 DFS 簡介
DFS( Device File System)是一種抽象的文件機制,RT-Thread中對文件系統的相關操作實際上都是通過操作DFS實現,也就是說DFS是對各種文件系統的抽象。DFS使的其他部分無須關心不同文件系統之間的差異,使得RT-Thread可以支持多種類型的文件系統。
3 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 標準的總稱。
虛擬文件系統層:
用戶可以將具體的文件系統注冊到 DFS 中,如 FatFS、RomFS、DevFS 等。
設備抽象層:設備抽象層將物理設備如 SD Card、SPI Flash、Nand Flash,抽象成符合文件系統能夠訪問的設備,例如 FAT 文件系統要求存儲設備必須是塊設備類型。
不同文件系統類型是獨立于存儲設備驅動而實現的,因此把底層存儲設備的驅動接口和文件系統對接起來之后,才可以正確地使用文件系統功能。
4 DFS 數據結構
文件系統操作表:
1const struct dfs_filesystem_ops *filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX];
文件系統表:
1struct dfs_filesystem filesystem_table[DFS_FILESYSTEMS_MAX];
文件描述符:
1static struct dfs_fdtable _fdtab;
5 虛擬文件系統使用步驟
初始化 DFS 組件。
注冊具體類型的文件系統。
掛載文件系統
當文件系統不再使用,可以將它卸載。
5.1 初始化 DFS 組件
dfs_init() 函數會初始化 DFS 所需的相關資源,創建一些關鍵的數據結構, 有了這些數據結構,DFS 便能在系統中找到特定的文件系統,并獲得對特定存儲設備內文件的操作方法。
dfs_init()加入了自動初始化機制,在系統上電后會自動運行dfs_init()。
1INIT_PREV_EXPORT(dfs_init);
實例化DFS 組件相關的數據結構
1const struct dfs_filesystem_ops *filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX];
1struct dfs_filesystem filesystem_table[DFS_FILESYSTEMS_MAX]
1static struct dfs_fdtable _fdtab
創建當前目錄表
1char working_directory[DFS_PATH_MAX] = {“/”}
初始化 DFS:
清除文件系統操作表
清除文件系統表
清除文件描述符表
初始化互斥量
設置當前工作目錄為“/”
5.2 注冊具體類型的文件系統
在 DFS 組件初始化之后,還需要初始化使用的具體類型的文件系統,也就是將具體類型的文件系統注冊到 DFS 中。注冊文件系統的接口如下所示:
1int dfs_register(const struct dfs_filesystem_ops *ops);
檢查這個文件系統是否已經存在于文件系統操作表的目錄中
在文件系統操作表中找出一個空的文件類型條目
將這個文件系統的數據結構地址賦值給空的文件系統操作表目錄
5.3 掛載文件系統
在掛載文件系統之前,如果是用作存儲設備,還需要先存儲設備注冊為塊設備,然后格式化成對應的文件系統后,才能掛載。
在 RT-Thread 中,掛載是指將一個存儲設備掛接到一個已存在的路徑上。我們要訪問存儲設備中的文件,必須將文件所在的分區掛載到一個已存在的路徑上,然后通過這個路徑來訪問存儲設備。掛載文件系統的接口如下所示:
1int dfs_mount(const char *device_name,
2 const char *path,
3 const char *filesystemtype,
4 unsigned long rwflag,
5 const void *data);
在文件系統操作表中找出特定的文件系統
為特殊文件系統建立完整路徑
檢查路徑是否存在
檢查文件系統是否掛載在文件系統表中
檢查文件系統表是否有空余,如果有,把空余地址指向給此文件系統
注冊文件系統
調用此文件系統的掛載接口
5.4 卸載文件系統
當某個文件系統不需要再使用了,那么可以將它卸載掉。卸載文件系統的接口如下所示:
1int dfs_unmount(const char *specialfile);
檢查路徑是否存在
在文件系統表中找到此文件系統
清除文件系統表的這個條目內容
調用此文件系統的卸載接口
6 文件系統實例演示
6.1 devfs文件系統
6.1.1 簡介
devfs是設備文件系統,設備文件系統是用來把一切設備都抽象為像文件那樣操作(如可讀,可寫)。devfs默認掛載在“/dev”路徑下。但是會發現在根目錄下使用shell 的ls命令不能看到“/dev”目錄, 但是cd “/dev” 能進入dev 目錄 ,并且進入dev目錄后也能顯示dev下的設備。因為根目錄“/“下并沒有創建任何文件夾,所以在根目錄下ls命令自然看不到“/dev”目錄。由于掛載文件系統需要掛載在一個已存在的路徑上,devfs屬于特殊文件系統,DFS為devfs設備文件系統注冊時設置了專門的“/dev”路徑以供設備文件系統掛載。
1 /* Check if the path exists or not, raw APIs call, fixme */
2 if ((strcmp(fullpath, ”/“) != 0) && (strcmp(fullpath, ”/dev“) != 0))
3 {
4 struct dfs_fd fd;
5
6 if (dfs_file_open(&fd, fullpath, O_RDONLY | O_DIRECTORY) 《 0)
7 {
8 rt_free(fullpath);
9 rt_set_errno(-ENOTDIR);
10
11 return -1;
12 }
13 dfs_file_close(&fd);
14 }
6.1.2 注冊devfs文件系統
如果開啟了DFS,devfs設備文件系統會在dfs_init()里自動初始化和掛載。
1#ifdef RT_USING_DFS_DEVFS
2 {
3 extern int devfs_init(void);
4
5 /* if enable devfs, initialize and mount it as soon as possible */
6 devfs_init();
7
8 dfs_mount(NULL, ”/dev“, ”devfs“, 0, 0);
9 }
10#endif
設置devfs文件系統的數據結構:_device_fs
1static const struct dfs_filesystem_ops _device_fs =
2{
3 ”devfs“,
4 DFS_FS_FLAG_DEFAULT,
5 &_device_fops,
6
7 dfs_device_fs_mount,
8 RT_NULL,
9 RT_NULL,
10 RT_NULL,
11
12 RT_NULL,
13 dfs_device_fs_stat,
14 RT_NULL,
15};
將devfs文件系統的數據結構掛載到文件系統操作表里
1int devfs_init(void)
2{
3 /* register rom file system */
4 dfs_register(&_device_fs);
5
6 return 0;
7}
檢查devfs文件系統是否已經存在于文件系統操作表的目錄中
在文件系統操作表中找出一個空的文件類型條目
將devfs文件系統的數據結構_device_fs地址賦值給空的文件系統操作表目錄
文件系統操作表filesystem_operation_table的第一個目錄:
6.1.2 掛載devfs文件系統
1dfs_mount(NULL, ”/dev“, ”devfs“, 0, 0)
在文件系統操作表中找出devfs文件系統
跳過檢查”/dev“路徑是否存在
檢查devfs文件系統是否已經掛載在文件系統表中
檢查文件系統表是否有空余,如果有,把空余地址賦值給devfs文件系統
注冊文件系統
調用devfs文件系統的掛載接口
文件系統表filesystem_table的第一個目錄:
6.1.3 測試devfs文件系統
在根目錄下使用shell 的cd命令切換到/dev目錄,然后使用ls命令:
6.2 RomFS文件系統
6.2.1 簡介
RomFS是在嵌入式設備上常用的一種文件系統,具備體積小,可靠性高,讀取速度快等優點,常用來作為系統初始文件系統。但也具有其局限性,RomFS是一種只讀文件系統。
6.2.2 注冊devfs文件系統
把RomFS當作初始文件系統rootfs掛載在根目錄,在RomFS里創建幾個目錄,用于其他文件系統的掛載點。
更改… omfs.c文件,添加mnt文件夾和user文件夾。
1#include 《rtthread.h》
2#include 《dfs_romfs.h》
3
4static const struct romfs_dirent _romfs_root[] = {
5 {ROMFS_DIRENT_DIR, ”mnt“, RT_NULL, 0},
6 {ROMFS_DIRENT_DIR, ”user“, RT_NULL, 0}
7};
8
9const struct romfs_dirent romfs_root = {
10 ROMFS_DIRENT_DIR, ”/“, (rt_uint8_t *)_romfs_root, sizeof(_romfs_root)/sizeof(_romfs_root[0])
11};
設置romfs文件系統的數據結構:_romfs
1static const struct dfs_filesystem_ops _romfs =
2{
3 ”rom“,
4 DFS_FS_FLAG_DEFAULT,
5 &_rom_fops,
6
7 dfs_romfs_mount,
8 dfs_romfs_unmount,
9 NULL,
10 NULL,
11
12 NULL,
13 dfs_romfs_stat,
14 NULL,
15};
將romfs文件系統的數據結構掛載到文件系統操作表里
1int dfs_romfs_init(void)
2{
3 /* register rom file system */
4 dfs_register(&_romfs);
5 return 0;
6}
7INIT_COMPONENT_EXPORT(dfs_romfs_init);//自動初始化
檢查romfs文件系統是否已經存在于文件系統操作表的目錄中
在文件系統操作表中找出一個空的文件類型條目
將romfs文件系統的數據結構_romfs地址賦值給空的文件系統操作表目錄
文件系統操作表filesystem_operation_table的第二個目錄:
6.2.3 掛載romfs文件系統
1int mnt_init(void)
2{
3 if(dfs_mount (RT_NULL,”/“,”rom“,0,&(romfs_root)) == 0)
4 {
5 rt_kprintf(”ROM file system initializated;
“);
6 }
7 else
8 {
9 rt_kprintf(”ROM file system initializate failed;
“);
10 }
11 return 0;
12}
13INIT_ENV_EXPORT(mnt_init);
在文件系統操作表中找出romfs文件系統
檢查”/“路徑是否存在
檢查romfs文件系統是否已經掛載在文件系統表中
檢查文件系統表是否有空余,如果有,把空余地址指向romfs文件系統
注冊文件系統
調用romfs文件系統的掛載接口
文件系統表filesystem_table的第二個目錄:
6.2.4 測試RomFS文件系統
在根目錄下使用shell 的ls命令:
6.3 RamFS文件系統
6.3.1 簡介
RamFS是內存文件系統,它不能格式化,可以同時創建多個,在創建時可以指定其最大能使用的內存大小。其優點是讀寫速度很快,但存在掉電丟失的風險。如果一個進程的性能瓶頸是硬盤的讀寫,那么可以考慮在RamFS上進行大文件的讀寫操作。
RT-Thread的RamFS設計之初未考慮支持文件夾,所以不能使用mkdir。
6.3.2 注冊RamFS文件系統
設置ramfs文件系統的數據結構:_ramfs
1static const struct dfs_filesystem_ops _ramfs =
2{
3 ”ram“,
4 DFS_FS_FLAG_DEFAULT,
5 &_ram_fops,
6
7 dfs_ramfs_mount,
8 dfs_ramfs_unmount,
9 NULL, /* mkfs */
10 dfs_ramfs_statfs,
11
12 dfs_ramfs_unlink,
13 dfs_ramfs_stat,
14 dfs_ramfs_rename,
15};
將ramfs文件系統的數據結構掛載到文件系統操作表里
1int dfs_ramfs_init(void)
2{
3 /* register ram file system */
4 dfs_register(&_ramfs);
5
6 return 0;
7}
8INIT_COMPONENT_EXPORT(dfs_ramfs_init);
檢查ramfs文件系統是否已經存在于文件系統操作表的目錄中
在文件系統操作表中找出一個空的文件類型條目
將ramfs文件系統的數據結構_ramfs地址賦值給空的文件系統操作表目錄
文件系統操作表filesystem_operation_table的第三個目錄:
6.3.3 掛載RamFS文件系統
1int mnt_ram_elminit(void)
2{
3 if(dfs_mount (RT_NULL,”/mnt“,”ram“,0,dfs_ramfs_create(rampool, 1024)) == 0)
4 {
5 rt_kprintf(”ram file system initializated;
“);
6 }
7 else
8 {
9 rt_kprintf(”ram file system initializate failed;
“);
10 }
11 return 0;
12}
13INIT_ENV_EXPORT(mnt_ram_elminit);
在文件系統操作表中找出ramfs文件系統
檢查”/mnt“路徑是否存在
檢查ramfs文件系統是否已經掛載在文件系統表中
檢查文件系統表是否有空余,如果有,把空余地址指向ramfs文件系統
注冊文件系統
調用ramfs文件系統的掛載接口
文件系統表filesystem_table的第三個目錄:
6.3.4 測試RamFS文件系統
在根目錄下使用shell 的cd命令切換到/mnt目錄,然后使用ls命令:
6.4 elm-FAT文件系統
6.4.1 簡介
FatFs 是一個通用的文件系統(FAT/exFAT)模塊,用于在小型嵌入式系統中實現FAT文件系統。
6.4.2 使用流程
初始化 DFS 組件。
初始化具體類型的文件系統。
在存儲器上創建塊設備。
格式化塊設備。
掛載塊設備到 DFS 目錄中。
當文件系統不再使用,可以將它卸載
6.4.3 注冊elm-FAT文件系統
設置fatfs文件系統的數據結構:dfs_elm
1static const struct dfs_filesystem_ops dfs_elm =
2{
3 ”elm“,
4 DFS_FS_FLAG_DEFAULT,
5 &dfs_elm_fops,
6
7 dfs_elm_mount,
8 dfs_elm_unmount,
9 dfs_elm_mkfs,
10 dfs_elm_statfs,
11
12 dfs_elm_unlink,
13 dfs_elm_stat,
14 dfs_elm_rename,
15};
將fatfs文件系統的數據結構掛載到文件系統操作表里
1int elm_init(void)
2{
3 /* register fatfs file system */
4 dfs_register(&dfs_elm);
5
6 return 0;
7}
8INIT_COMPONENT_EXPORT(elm_init);
檢查fatfs文件系統是否已經存在于文件系統操作表的目錄中
在文件系統操作表中找出一個空的文件類型條目
將fatfs文件系統的數據結構dfs_elm 地址賦值給空的文件系統操作表目錄
elm-FAT文件系統注冊過程如下圖所示:
6.4.4 掛載elm-FAT文件系統
1void sd_mount(void *parameter)
2{
3 while (1)
4 {
5 rt_thread_mdelay(500);
6 if(rt_device_find(”sd0“) != RT_NULL)
7 {
8 if (dfs_mount(”sd0“, ”/fatfs“, ”elm“, 0, 0) == RT_EOK)
9 {
10 LOG_I(”sd card mount to ‘/fatfs’“);
11 break;
12 }
13 else
14 {
15 LOG_W(”sd card mount to ‘/fatfs’ failed!“);
16 }
17 }
18 }
19}
20
21int stm32_sdcard_mount(void)
22{
23 rt_thread_t tid;
24
25 tid = rt_thread_create(”sd_mount“, sd_mount, RT_NULL,
26 1024, RT_THREAD_PRIORITY_MAX - 2, 20);
27 if (tid != RT_NULL)
28 {
29 rt_thread_startup(tid);
30 }
31 else
32 {
33 LOG_E(”create sd_mount thread err!“);
34 }
35 return RT_EOK;
36}
37INIT_APP_EXPORT(stm32_sdcard_mount);
在文件系統操作表中找出elm文件系統
檢查”/fatfs“路徑是否存在
檢查elm文件系統是否已經掛載在文件系統表中
檢查文件系統表是否有空余,如果有,把空余地址指向elm文件系統
注冊文件系統
調用elm文件系統的掛載接口
6.4.5 測試elm-FAT文件系統
在根目錄下使用shell 的cd命令切換到/fatfs目錄,然后使用ls命令:
6.5 littlefs文件系統
6.5.1 簡介
littlefs 是 ARM 官方推出的,專為嵌入式系統設計的文件系統,相比傳統的文件系統,littlefs 具有以下優點:
自帶擦寫均衡
支持掉電保護
占用的 RAM/ROM 少
littlefs 自帶的擦寫均衡和掉電保護使開發者可以放心的將文件系統掛載到 nor flash 上。層級關系
littlefs 在 RT-Thread 上運行的層級關系圖如下所示:
6.5.2 使用流程
初始化 DFS 組件。
使能 littlefs 軟件包。
使能 MTD 設備。
使能 fal,用來創建 MTD 設備。
創建 MTD 設備
掛載MTD設備到 DFS 目錄中。
6.5.3 注冊littlefs文件系統
設置littlefs文件系統的數據結構:_dfs_lfs_ops
1static const struct dfs_filesystem_ops _dfs_lfs_ops = {
2 ”lfs“,
3 DFS_FS_FLAG_DEFAULT,
4 &_dfs_lfs_fops,
5
6 _dfs_lfs_mount,
7 _dfs_lfs_unmount,
8
9 _dfs_lfs_mkfs,
10 _dfs_lfs_statfs,
11 _dfs_lfs_unlink,
12 _dfs_lfs_stat,
13 _dfs_lfs_rename,
14};
將littlefs文件系統的數據結構掛載到文件系統操作表里
1int dfs_lfs_init(void)
2{
3 /* init file system lock */
4 rt_mutex_init(&_lfs_lock, ”lfsmtx“, RT_IPC_FLAG_FIFO);
5 /* register ram file system */
6 return dfs_register(&_dfs_lfs_ops);
7}
8INIT_COMPONENT_EXPORT(dfs_lfs_init);
檢查littlefs文件系統是否已經存在于文件系統操作表的目錄中
在文件系統操作表中找出一個空的文件類型條目
將littlefs文件系統的數據結構_dfs_lfs_ops 地址賦值給空的文件系統操作表目錄
6.5.4 掛載littlefs文件系統
1 。..
2 struct rt_device *mtd_dev = RT_NULL;
3
4 。..
5 /* 初始化 fal */
6 fal_init();
7 /* 生成 mtd 設備 */
8 mtd_dev = fal_mtd_nor_device_create(FS_PARTITION_NAME);
9 if (!mtd_dev)
10 {
11 LOG_E(”Can‘t create a mtd device on ’%s‘ partition.“, FS_PARTITION_NAME);
12 }
13 else
14 {
15 /* 掛載 littlefs */
16 if (dfs_mount(FS_PARTITION_NAME, ”/littlefs“, ”lfs“, 0, 0) == 0)
17 {
18 LOG_I(”Filesystem initialized!“);
19 }
20 else
21 {
22 /* 格式化文件系統 */
23 dfs_mkfs(”lfs“, FS_PARTITION_NAME);
24 /* 掛載 littlefs */
25 if (dfs_mount(”filesystem“, ”/littlefs“, ”lfs“, 0, 0) == 0)
26 {
27 LOG_I(”Filesystem initialized!“);
28 }
29 else
30 {
31 LOG_E(”Failed to initialize filesystem!“);
32 }
33 }
34 }
35 。..
在文件系統操作表中找出lfs文件系統
檢查”/littlefs“路徑是否存在
檢查littlefs文件系統是否已經掛載在文件系統表中
檢查文件系統表是否有空余,如果有,把空余地址指向littlefs文件系統
注冊文件系統
調用littlefs文件系統的掛載接口
6.5.5 測試littlefs文件系統
在根目錄下使用shell 的cd命令切換到/littlefs目錄,然后使用ls命令:
注意:spi_flash.h中缺少一個頭文件,需要自行添加
6.6 文件系統綜合例程
DevFS、RomFS、RamFS、FatFS文件系統配置:
littlefs文件系統配置;
主程序:
1#include 《rtthread.h》
2#include 《rtdevice.h》
3#include 《board.h》
4#include 《fal.h》
5
6#include 《dfs_fs.h》
7#include 《dfs_romfs.h》
8#include 《dfs_ramfs.h》
9#include 《dfs_posix.h》
10
11#define DBG_TAG ”main“
12#define DBG_LVL DBG_LOG
13#include 《rtdbg.h》
14
15/* defined the LED0 pin: PH10 */
16#define LED0_PIN GET_PIN(H, 10)
17
18int main(void)
19{
20 int count = 1;
21 rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
22
23 while (count++)
24 {
25 rt_pin_write(LED0_PIN, PIN_HIGH);
26 rt_thread_mdelay(1000);
27 rt_pin_write(LED0_PIN, PIN_LOW);
28 rt_thread_mdelay(1000);
29 }
30
31 return RT_EOK;
32}
33rt_uint8_t rampool[128];
34
35void sd_mount(void *parameter)
36{
37 while (1)
38 {
39 rt_thread_mdelay(500);
40 if(rt_device_find(”sd0“) != RT_NULL)
41 {
42 if (dfs_mount(”sd0“, ”/fatfs“, ”elm“, 0, 0) == RT_EOK)
43 {
44 LOG_I(”sd card mount to ’/fatfs‘“);
45 break;
46 }
47 else
48 {
49 LOG_W(”sd card mount to ’/fatfs‘ failed!“);
50 }
51 }
52 }
53}
54
55int fs_init(void)
56{
57 /* partition initialized */
58 fal_init();
59
60 if(dfs_mount (RT_NULL,”/“,”rom“,0,&(romfs_root)) == 0)
61 {
62 LOG_I(”ROM file system initializated;
“);
63 }
64 else
65 {
66 LOG_I(”ROM file system initializate failed;
“);
67 }
68
69 if(dfs_mount (RT_NULL,”/ram“,”ram“,0,dfs_ramfs_create(rampool, sizeof(rampool))) == 0)
70 {
71 LOG_I(”ram file system initializated;
“);
72 }
73 else
74 {
75 LOG_I(”ram file system initializate failed;
“);
76 }
77
78 /* Create a block device on the file system partition of spi flash */
79 struct rt_device *flash_dev = fal_mtd_nor_device_create(”filesystem“);
80
81 if (flash_dev == RT_NULL)
82 {
83 LOG_I(”Can’t create a mtd device on ‘%s’ partition.“, ”filesystem“);
84 }
85 else
86 {
87 LOG_I(”Create a mtd device on the %s partition of flash successful.“, ”filesystem“);
88 }
89 /* mount the file system from ”filesystem“ partition of spi flash. */
90 if (dfs_mount(flash_dev-》parent.name, ”/littlefs“, ”lfs“, 0, 0) == 0)
91 {
92 LOG_I(”littlefs initialized!“);
93 }
94 else
95 {
96 dfs_mkfs(”lfs“, flash_dev-》parent.name);
97 if (dfs_mount(flash_dev-》parent.name, ”/“, ”lfs“, 0, 0) == 0)
98 {
99 LOG_I(”littlefs initialized!“);
100 }
101 }
102
103 rt_thread_t tid;
104
105 tid = rt_thread_create(”sd_mount“, sd_mount, RT_NULL,
106 1024, RT_THREAD_PRIORITY_MAX - 2, 20);
107 if (tid != RT_NULL)
108 {
109 rt_thread_startup(tid);
110 }
111 else
112 {
113 LOG_E(”create sd_mount thread err!“);
114 }
115 return 0;
116}
117INIT_COMPONENT_EXPORT(fs_init);
測試:
? ? ? ?責任編輯:pj
評論
查看更多