文件系統(tǒng)(File System)是計算機系統(tǒng)必不可少的組成部分,可以說除了部分結(jié)構(gòu)簡單的單片機系統(tǒng)之外,文件系統(tǒng)是支撐每一個計算機系統(tǒng)運行的最重要的支撐,無論是操作系統(tǒng)、應(yīng)用程序、文檔還是音視頻都是基于文件系統(tǒng)的。所以由此可見文件系統(tǒng)在計算機上的重要地位。
嵌入式系統(tǒng)上有很多場合也需要使用大容量存儲設(shè)備,這時候可以直接使用存儲設(shè)備的讀寫API來進行數(shù)據(jù)的保存和讀取,但是這樣做的很大不足是無法和電腦系統(tǒng)的文件兼容,如果想將電腦系統(tǒng)上的一張圖片或者txt文檔存入嵌入式系統(tǒng)使用的存儲器中去就會很麻煩,但是如果這個嵌入式存儲器使用的是兼容FAT文件系統(tǒng)的存儲格式那么氣與個人電腦的文件交換就會大大方便。例如一般場合使用的TF卡,如果直接調(diào)用讀和寫函數(shù)進行TF的存取也不是不可以,但是如果這個TF卡上有FAT文件系統(tǒng)的話就可以使用讀卡器從電腦上直接拷貝文件,大大方便了文件的交互。
鑒于文件系統(tǒng)的重要性,有必要好好分析一下文件系統(tǒng)的組成原理,下面以當(dāng)前主流的windows文件系統(tǒng)FAT32為對象,分析一下文件系統(tǒng)的存儲機制。
下面是對FAT32文件系統(tǒng)的一個簡要介紹:FAT32 文件系統(tǒng)您一定不會陌生,最多看到它是在 windows 操作系統(tǒng)里,但在一些嵌入式產(chǎn)品(如手機、MP3、MP4 等)中,也能看到它的身影。從某種意義上來講,F(xiàn)AT32文件系統(tǒng)是非常成功的,使我們可以脫離底層儲存設(shè)備驅(qū)動,更為方便高效地組織數(shù)據(jù)。給單片機系統(tǒng)中的大容量存儲器(如 SD 卡、CF 卡、硬盤等)配以 FAT32 文件系統(tǒng),將是非常有意義的(如創(chuàng)建的數(shù)據(jù)文件可以在 windows 等操作系統(tǒng)中直接讀取等)。
我手上有一張512MB的TF卡將其插在電腦上使用二進制查看軟件打開TF卡,下面是拷貝的前512字節(jié)的數(shù)據(jù)進行解說:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 EB 58 90 4D 53 44 4F 53 35 2E 30 00 02 08 74 18 ëX.MSDOS5.0...t.
00000010 02 00 00 00 00 F8 00 00 3F 00 FF 00 00 00 00 00 .....ø..?.ÿ.....
00000020 00 34 0F 00 C6 03 00 00 00 00 00 00 02 00 00 00 .4..Æ...........
00000030 01 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000040 80 01 29 22 BE A5 F6 4E 4F 20 4E 41 4D 45 20 20 €.)"¾¥öNO NAME
00000050 20 20 46 41 54 33 32 20 20 20 33 C9 8E D1 BC F4 FAT32 3É?Ѽô
00000060 7B 8E C1 8E D9 BD 00 7C 88 56 40 88 4E 02 8A 56 {?Á?Ù½.|ˆV@ˆN.ŠV
00000070 40 B4 41 BB AA 55 CD 13 72 10 81 FB 55 AA 75 0A @´A»ªUÍ.r..ûUªu.
00000080 F6 C1 01 74 05 FE 46 02 EB 2D 8A 56 40 B4 08 CD öÁ.t.þF.ë-ŠV@´.Í
00000090 13 73 05 B9 FF FF 8A F1 66 0F B6 C6 40 66 0F B6 .s.¹ÿÿŠñf.¶Æ@f.¶
000000A0 D1 80 E2 3F F7 E2 86 CD C0 ED 06 41 66 0F B7 C9 Ñ€â?÷â†ÍÀí.Af.·É
000000B0 66 F7 E1 66 89 46 F8 83 7E 16 00 75 39 83 7E 2A f÷áf‰Føƒ~..u9ƒ~*
000000C0 00 77 33 66 8B 46 1C 66 83 C0 0C BB 00 80 B9 01 .w3f‹F.fƒÀ.».€¹.
000000D0 00 E8 2C 00 E9 A8 03 A1 F8 7D 80 C4 7C 8B F0 AC .è,.é¨.¡ø}€Ä|‹ð¬
000000E0 84 C0 74 17 3C FF 74 09 B4 0E BB 07 00 CD 10 EB „Àt.<ÿt.´.»..Í.ë
000000F0 EE A1 FA 7D EB E4 A1 7D 80 EB DF 98 CD 16 CD 19 î¡ú}ëä¡}€ëߘÍ.Í.
00000100 66 60 80 7E 02 00 0F 84 20 00 66 6A 00 66 50 06 f`€~...„ .fj.fP.
00000110 53 66 68 10 00 01 00 B4 42 8A 56 40 8B F4 CD 13 Sfh....´BŠV@‹ôÍ.
00000120 66 58 66 58 66 58 66 58 EB 33 66 3B 46 F8 72 03 fXfXfXfXë3f;Før.
00000130 F9 EB 2A 66 33 D2 66 0F B7 4E 18 66 F7 F1 FE C2 ùë*f3Òf.·N.f÷ñþÂ
00000140 8A CA 66 8B D0 66 C1 EA 10 F7 76 1A 86 D6 8A 56 ŠÊf‹ÐfÁê.÷v.†ÖŠV
00000150 40 8A E8 C0 E4 06 0A CC B8 01 02 CD 13 66 61 0F @ŠèÀä..̸..Í.fa.
00000160 82 74 FF 81 C3 00 02 66 40 49 75 94 C3 42 4F 4F ‚tÿ.Ã..f@Iu”ÃBOO
00000170 54 4D 47 52 20 20 20 20 00 00 00 00 00 00 00 00 TMGR ........
00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000001A0 00 00 00 00 00 00 00 00 00 00 00 00 0D 0A 44 69 ..............Di
000001B0 73 6B 20 65 72 72 6F 72 FF 0D 0A 50 72 65 73 73 sk errorÿ..Press
000001C0 20 61 6E 79 20 6B 65 79 20 74 6F 20 72 65 73 74 any key to rest
000001D0 61 72 74 0D 0A 00 00 00 00 00 00 00 00 00 00 00 art.............
000001E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000001F0 00 00 00 00 00 00 00 00 AC 01 B9 01 00 00 55 AA ........¬.¹...Uª
下面講講最重要的一個DBR(DOS BOOT RECORD 操作系統(tǒng)引導(dǎo)記錄區(qū))。DBR 是我們進軍 FAT32 的首道防線。其實 DBR 中的 BPB 部分才是這一區(qū)域的核心部分(第12~90 字節(jié)為 BPB),只有深入詳實地理解了 BPB 的意義,才能夠更好地實現(xiàn)和操控 FAT32。關(guān)于 DBR 在 FAT32 中的地位就不多說了,上面的數(shù)據(jù)中的前90個字節(jié)是BPB的主要部分。BPB的C語言結(jié)構(gòu)體如下所示:
struct FAT32_DBR
{
unsigned char BS_jmpBoot[3]; //跳轉(zhuǎn)指令 offset: 0
unsigned char BS_OEMName[8]; //原始制造商 offset: 3
unsigned char BPB_BytesPerSec[2]; //每扇區(qū)字節(jié)數(shù) offset:11
unsigned char BPB_SecPerClus[1]; //每簇扇區(qū)數(shù) offset:13
unsigned char BPB_RsvdSecCnt[2]; //保留扇區(qū)數(shù)目 offset:14
unsigned char BPB_NumFATs[1]; //此卷中FAT表數(shù) offset:16
unsigned char BPB_RootEntCnt[2]; //FAT32為0 offset:17
unsigned char BPB_TotSec16[2]; //FAT32為0 offset:19
unsigned char BPB_Media[1]; //存儲介質(zhì) offset:21
unsigned char BPB_FATSz16[2]; //FAT32為 0 offset:22
unsigned char BPB_SecPerTrk[2]; //磁道扇區(qū)數(shù) offset:24
unsigned char BPB_NumHeads[2]; //磁頭數(shù) offset:26
unsigned char BPB_HiddSec[4]; //FAT區(qū)前隱扇區(qū)數(shù) offset:28
unsigned char BPB_TotSec32[4]; //該卷總扇區(qū)數(shù) offset:32
unsigned char BPB_FATSz32[4]; //一個FAT表扇區(qū)數(shù) offset:36
unsigned char BPB_ExtFlags[2]; //FAT32特有 offset:40
unsigned char BPB_FSVer[2]; //FAT32特有 offset:42
unsigned char BPB_RootClus[4]; //根目錄簇號 offset:44
unsigned char FSInfo[2]; //保留扇區(qū)FSINFO扇區(qū)數(shù) offset:48
unsigned char BPB_BkBootSec[2]; //通常為6 offset:50
unsigned char BPB_Reserved[12]; //擴展用 offset:52
unsigned char BS_DrvNum[1]; //offset:64
unsigned char BS_Reserved1[1]; //offset:65
unsigned char BS_BootSig[1]; //offset:66
unsigned char BS_VolID[4]; //offset:67
unsigned char BS_FilSysType[11]; //offset:71
unsigned char BS_FilSysType1[8]; //"FAT32 " offset:82
};
每個字段的大小和含義都有詳細的注釋,解析這些數(shù)據(jù)使是們進行文件系統(tǒng)探秘的首要任務(wù)。需要注意的是在BPB中數(shù)據(jù)是以小端模式存儲的,所以51單片機的大端模式下需要進行大小端的轉(zhuǎn)換,而一般ARM的默認(rèn)方式就是小端模式,所以無需進行轉(zhuǎn)換操作。
簡單介紹DBR的BPB之后還需要介紹一下FAT表(文件分配表)的概念。什么是文件分配表呢?顧名思義,就是給文件分配存儲空間的表,這里面存放的不是文件的數(shù)據(jù)而是文件所在的簇的信息,具體下面會給出解釋。
FAT 表是 FAT32 文件系統(tǒng)中用于磁盤數(shù)據(jù)(文件)索引和定位引進的一種鏈?zhǔn)浇Y(jié)構(gòu)。可以說 FAT 表是 FAT32 文件系統(tǒng)最有特色的一部分,它的鏈?zhǔn)酱鎯C制也是 FAT32 的精華所在,也正因為有了它才使得數(shù)據(jù)的存儲可以不連續(xù),使磁盤的功能發(fā)揮得更為出色。
那么FAT 表到底在什么地方?它到底是什么樣子的?這時就要回到剛才的BPB部分了,在BPB中有一個字段叫做BPB_RsvdSecCnt,這個字段是表示保留山區(qū)數(shù)目,其實就是保留給BPB使用的空間的扇區(qū)數(shù)量,也就是說這個數(shù)值表示的就是FAT表前面的空間大小,那么FAT表的地址就是這個字段的數(shù)值了。其實在文件系統(tǒng)中為了保證正確性和穩(wěn)定性胡設(shè)置兩個完全相同的FAT表,并且兩個FAT是同步的,也就是說對一個 FAT 表的操作, 同樣地, 也應(yīng)該在另一個 FAT表進行相同的操作,時刻保證它們內(nèi)容的一致。這樣是為了安全起見,當(dāng)一個FAT 因為一些原因而遭到破壞的時候,可以從另一個 FAT 表進行恢復(fù)。
FAT 表的內(nèi)容如下圖所示:
上圖就是一個實際的 FAT 表。前 8 個字節(jié)“F8 FF FF 0F FF FF FF FF”為FAT32 的 FAT 表頭標(biāo)記,用以表示此處是 FAT 表的開始。后面的數(shù)據(jù)每四個字節(jié)為一個簇項(從第 2 簇開始) ,用以標(biāo)記此簇的下一個簇號。
上面講了很多, 都是圍繞簇這樣一個詞來講的, 簇又是什么?為什么要將它引入到 FAT32 里來呢?磁盤上最小可尋址存儲單元稱為扇區(qū), 通常每個扇區(qū)為 512 個字節(jié)。 由于多數(shù)文件比扇區(qū)大得多, 因此如果對一個文件分配最小的存儲空間, 將使存儲器能存儲更多數(shù)據(jù),這個最小存儲空間即稱為簇。根據(jù)存儲設(shè)備(磁盤、閃卡和硬盤)的容量,簇的大小可以不同以使存儲空間得到最有效的應(yīng)用。在早期的 360KB 磁盤上,簇大小為 2 個扇區(qū)(1,024 字節(jié));第一批的 10MB 硬盤的簇大小增加到8個扇區(qū)(4,096字節(jié)); 現(xiàn)在的小型閃存設(shè)備上的典型簇大小是8KB或 16KB。2GB 以上的硬盤驅(qū)動器有 32KB 的簇。如果對于容量大的存儲定義了比較小的簇的話,就會使 FAT 表的體積很大,從而造成數(shù)據(jù)的冗余和效率的下降。
需要指出的是,簇作為 FAT32 進行數(shù)據(jù)存儲的最小單位,內(nèi)部扇區(qū)是不能進一步細分的, 即使一個文件的數(shù)據(jù)寫到一個簇中后, 簇中還有容量的剩余(里部扇區(qū)沒有寫滿) ,哪怕這個簇只寫了一個字節(jié),其它文件的數(shù)據(jù)也是不能接在后面繼續(xù)數(shù)據(jù)的,而只能另外找沒有被占用的簇。
那么 FAT 表有多大呢?FAT 表中每四個字節(jié)表示一個簇,所以 FAT 表的大小由實際的簇數(shù)來決定。從這里也可以看出,如果簇過大,則 FAT 表比較小,但會造成空間的浪費,而如果簇過小,可以減小空間的浪費,但會使FAT 表變得臃腫。FAT 表的大小可以從 BPB 參數(shù) BPB_FATSz32讀出。
現(xiàn)在知道了FAT表的地址和FAT表的大小,也知道有兩個FAT表就可以計算出第一個文件夾也就是根文件夾的位置,他的位置就在FAT表的正后方,計算方法如下:
FirstDirSector = FirstFATSector + BPB_NumFATs[0] * FATsectors
其中FirstFATSector 表示FAT表的位置, BPB_NumFATs[0] 表示FAT表的數(shù)量,F(xiàn)ATsectors 表示的是FAT表占用的扇區(qū)數(shù)量。其實根目錄所在的簇就是第2號簇。計算出了根文件夾的位置就可以從根文件夾中讀取數(shù)據(jù)。在 FAT32 中其實已經(jīng)把文件的概念進行擴展,目錄同樣也是文件,根目錄的地位與其它目錄是相同的, 因此根目錄也被看作是文件。 既然是文件就會有文件名,根目錄的名稱就是磁盤的卷標(biāo)。
下面所以說根目錄文件的內(nèi)容。首先目錄也是文件,它可以看做是特殊文件,這個文件使用來存放其他文件或目錄的信息,例如文件名、擴展名、屬性、創(chuàng)建時間、最后修改時間、文件起始簇號、文件長度等等。所以我要讀取一個文件信息的時候首先要做的就是從目錄中讀取文件的信息數(shù)據(jù)。根據(jù)上面說的我們已經(jīng)獲取到了根目錄的地址,那么就可以從根目錄中讀取根目錄下的文件信息。每個文件/目錄在目錄文件中使用32個字節(jié)的數(shù)據(jù)表示,具體字段如下所示:
這樣就可以從根目錄下讀取各個文件,我們作為試驗不設(shè)計嵌套目錄的結(jié)構(gòu),只在根目錄下進行文件查看、讀寫試驗。所以在我的512M的TF卡上根目錄下有一個test.txt文件,根目錄的數(shù)據(jù)如下所示:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00400000 42 20 00 49 00 6E 00 66 00 6F 00 0F 00 72 72 00 B .I.n.f.o...rr.
00400010 6D 00 61 00 74 00 69 00 6F 00 00 00 6E 00 00 00 m.a.t.i.o...n...
00400020 01 53 00 79 00 73 00 74 00 65 00 0F 00 72 6D 00 .S.y.s.t.e...rm.
00400030 20 00 56 00 6F 00 6C 00 75 00 00 00 6D 00 65 00 .V.o.l.u...m.e.
00400040 53 59 53 54 45 4D 7E 31 20 20 20 16 00 23 BB 58 SYSTEM~1 ..#»X
00400050 7E 49 7E 49 00 00 BC 58 7E 49 03 00 00 00 00 00 ~I~I..¼X~I......
00400060 54 45 53 54 20 20 20 20 54 58 54 20 18 23 C0 58 TEST TXT .#ÀX
00400070 7E 49 81 49 00 00 5A 57 7E 49 05 00 13 20 00 00 ~I.I..ZW~I... ..
00400080 54 41 4E 47 51 55 41 4E 20 20 20 08 00 00 00 00 TANGQUAN .....
00400090 00 00 00 00 00 00 DA 58 7E 49 00 00 00 00 00 00 ......ÚX~I......
004000A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
我們可以看到在根目錄下的0x60到0x7F處表示的就是根目錄下的text.txt文件,0x80到0x9F處則表示的是根目錄。前面的兩個文件信息表示的可能是一些隱藏的文件,具體我也沒了解是什么。反正至此已經(jīng)可以從FAT32文件系統(tǒng)中成功獲取根目錄下的文件的信息,下一步就是根據(jù)這個文件的信息尋找文件存儲的位置,讀取文件內(nèi)容。
32個字節(jié)的文件信息已經(jīng)給得很明確了,確定了文件的其實簇號之后就可以進入FAT表中尋找那個簇以及文件的簇鏈,根據(jù)文件長度字段可以知道文件占用多少個簇,其實根據(jù)簇的鏈?zhǔn)酱鎯Y(jié)果就可以知道文件占用的簇數(shù)目,但是并不知道文件在最后一個簇中占用了多少個字節(jié),所以文件長度字段還是很有意義的。找到對應(yīng)的簇之后從對應(yīng)的簇中讀取數(shù)據(jù)即可,easy is'nt it?
原文地址:
https://blog.csdn.net/tq384998430/article/details/53414142