相关文章
《【SDIO】SDIO、SD卡、FatFs文件系统相关文章索引》
1.前言
本篇文章主要是介绍FAT 格式分区内数据是如何存储的。FAT 分区格式是Microsoft 最早支持的分区格式,依据 FAT 表中每个簇链的所占位数(有关概念,后面会讲到)分为 FAT12、 FAT16、 FAT32 三种格式"变种",但其基本存储方式是相似的。我们在嵌入式系统中使用的最多的是FAT32,所以后面主要介绍的是FAT32。
在介绍 FAT32文件系统前,我们需要先了解一下硬盘基本知识:磁头、磁道、扇区、柱面等,方便后面对FAT32文件系统的理解。
2.硬盘基本知识
硬盘基本概念包括:
- 盘片(platter)
- 磁头(head)
- 磁道(track)
- 扇区(sector)
- 柱面(cylinder)
- 盘片 片面 和 磁头
硬盘中一般会有多个盘片组成,每个盘片包含两个面,每个盘面都对应地有一个读/写磁头。受到硬盘整体体积和生产成本的限制,盘片数量都受到限制,一般都在5片以内。盘片的编号自下向上从0开始,如最下边的盘片有0面和1面,再上一个盘片就编号为2面和3面。如下图:
- 扇区 和 磁道
下图显示的是一个盘面,盘面中一圈圈灰色同心圆为一条条磁道,从圆心向外画直线,可以将磁道划分为若干个弧段,每个磁道上一个弧段被称之为一个扇区(图践绿色部分)。扇区是磁盘的最小组成单元,通常是512字节。(由于不断提高磁盘的大小,部分厂商设定每个扇区的大小是4096字节)
- 磁头 和 柱面
硬盘通常由重叠的一组盘片构成,每个盘面都被划分为数目相等的磁道,并从外缘的“0”开始编号,具有相同编号的磁道形成一个圆柱,称之为磁盘的柱面。磁盘的柱面数与一个盘面上的磁道数是相等的。由于每个盘面都有自己的磁头,因此,盘面数等于总的磁头数。 如下图
- 磁盘容量计算
存储容量 = 磁头数 × 磁道(柱面)数 × 每道扇区数 × 每扇区字节数
每个磁道的扇区数一样是说的老的硬盘,外圈的密度小,内圈的密度大,每圈可存储的数据量是一样的。
新的硬盘数据的密度都一致,这样磁道的周长越长,扇区就越多,存储的数据量就越大。
- 块/簇
磁盘块/簇是虚拟出来的概念,逻辑上的定义,多个扇区组合成一个块/簇。 扇区是物理上的概念,物理擦除、读写的最小单位。块/簇是操作系统中最小的逻辑存储单位。操作系统与磁盘打交道的最小单位是磁盘块/簇。
在Windows下如NTFS等文件系统中叫做在簇(cluster);
在在Linux下如Ext4等文件系统中叫做在块(block)。
每个簇或者块可以包括2、4、8、16、32、64…2的n次方个扇区。
3.FAT卷的组织架构介绍
一个用FAT格式化的卷组织如下图所示:
下表描述了FAT卷上的每个组织结构:
组件 | 描述 |
Boot Sector | 引导扇区:包含BIOS参数块,该参数块存储有关卷布局和文件系统结构的信息, 以及加载Windows 的引导代码。 |
Reserved Sectors | 保留扇区:在第一个FAT开始之前的扇区数量,包括引导扇区。 |
FAT 1 | 原始FAT表(可以简单理解FAT表为目录表,记录数据存储的位置。) |
FAT 2 (Duplicate) | FAT表的备份(为了防止原始FAT表被破坏后可以恢复) |
Root folder | 描述根目录分区中的文件和文件夹。 |
Other folders and all files | 包含文件系统中文件和文件夹的数据。 |
4.FAT32引导扇区(Boot Sector)
FAT32引导扇区在结构上与FAT16引导扇区非常相似,但是FAT32 BPB(BIOS Parameter Block)包含额外的字段。下表描述了用FAT32文件系统格式化的卷的引导扇区的各个部分。
字节位移 | 字段长度 | 字段名 |
0x00 | 3 个字节 | 跳转指令(Jump instruction) |
0x03 | 8 个字节 | 厂商标志和 os 版本号(OEM ID) |
0x0B | 53 个字节 | BPB(BIOS Parameter Block) |
0x40 | 26 个字节 | 扩展 BPB(Extended BPB) |
0x5A | 420 个字节 | 引导程序代码(Bootstrap code) |
0x01FE | 2 个字节 | 有效结束标志(End of sector marker) |
下面的示例演示了FAT32卷上的引导扇区的十六进制数据:
下面两个表说明了FAT32卷的BPB和扩展的BPB的布局,示例值对应于FAT32卷上引导扇区的数据。
FAT32 分区的 BPB 字段 | ||||||||
字节 位移 | 字段 长度 (字节) | 对应取值 | 名称和定义 | |||||
0x0B | 2 | 0x0200 | 扇区字节数(Bytes Per Sector) 硬件扇区的大小。 本字段合法的十进制值有 512、1024、2048 和 4096。对大多数磁盘来说,本字段的值为512。 | |||||
0x0D | 1 | 0x20 | 每簇扇区数(Sectors Per Cluster),一簇中的扇区数。 一个卷默认的簇大小取决于该卷的大小。本字段的合法十进制值有 1、2、4、8、16、32、64 和 128。 | |||||
0x0E | 2 | 0x092A | 保留扇区数(Reserved Sector) 第一个 FAT 开始之前的扇区数,包括引导扇区。 | |||||
0x10 | 1 | 0x02 | FAT 数(Number of FAT) 该分区上 FAT 的副本数。本字段的值一般为 2 | |||||
0x11 | 2 | 0x0000 | 根目录项数(Root Entries) 只有 FAT12/FAT16 使用此字段。对 FAT32 分区而言,本字段必须设置为 0 | |||||
0x13 | 2 | 0x0000 | 小扇区数(Small Sector) (只有 FAT12/FAT16 使用此字段)对 FAT32 分区而言,本字段必须设置为 0 | |||||
0x15 | 1 | 0xF8 | 媒体描述符( Media Descriptor) 提供有关媒体被使用的信息。值 0xF8 表示硬盘,0xF0 表示高密度的 3.5寸软盘。 | |||||
0x16 | 2 | 0x0000 | 每 FAT 扇区数(Sectors Per FAT) 只被 FAT12/FAT16 所使用,对 FAT32 分区而言,本字段必须设置为 0 | |||||
0x18 | 2 | 0x003F | 每道扇区数(Sectors Per Track) 包含使用 INT 13h 的磁盘的“每道扇区数”几何结构值。 该分区被多个磁头的柱面分成了多个磁道 | |||||
0x1A | 2 | 0x00FF | 磁头数(Number of Head) 本字段包含使用 INT 13h 的磁盘的“磁头数”几何结构值。 | |||||
0x1C | 4 | 0x0000003F | 隐藏扇区数(Hidden Sector) 该分区上引导扇区之前的扇区数。在引导序列计算到根目录的数据区的绝对位移的 过程中使用了该值。本字段一般只对那些在中断 13h 上可见的媒体有意义。在没有分区的媒体上它必须总是为 0 | |||||
0x20 | 4 | 0x03B723C1 | 总扇区数(Large Sector) 本字段包含 FAT32 分区中总的扇区数 | |||||
0x24 | 4 | 0x00003B6B | 每 FAT 扇区数(Sectors Per FAT) (只被 FAT32 使用)该分区每个 FAT 所占的扇区数。计算机利用这个数和 FAT 数以及保留扇区数(本表中所描述的)来决定根目录从哪里开始。该计算机还可以从目录中的项数决定该分区的 用户数据区从哪里开始 | |||||
0x28 | 2 | 0x0000 | 扩展标志(Extended Flag) (只被 FAT32 使用)该两个字节结构中各位的值为: 位 0-3:活动 FAT 数(从 0 开始计数,而不是 1).只有在不使用镜像时才有效 位 4-6:保留 位 7:0值意味着在运行时FAT被映射到所有的FAT,1值表示只有一个 FAT是活动的 位 8-15:保留 | |||||
0x2A | 2 | 0x0000 | 文件系统版本(File ystem Version) 只供 FAT32 使用,高字节是主要的修订号,而低字节是次要的修订号。 本字段支持将来对该 FAT32 媒体类型进行扩展。 | |||||
0x2C | 4 | 0x00000002 | 根目录簇号(Root Cluster Number) (只供 FAT32 使用) 根目录第一簇的簇号。 本字段的值一般为 2,但不总是如此 | |||||
0x30 | 2 | 0x0001 | 文件系统信息扇区号(File System Information Sector Number) (只供 FAT32 使用)FAT32 分区的保留区中的 文件系统信息(File System Information, FSINFO)结构的扇区号。其值一般为 1。在备份引导扇区(Backup Boot Sector)中保留了该 FSINFO 结构的一个副本,但是这个副本不保持更新 | |||||
0x32 | 2 | 0x0006 | 备份引导扇区(Backup Boot Sector) 非“0”表示存储引导扇区副本的扇区号。该字段的值通常为6。 | |||||
0x34 | 12 | 12 个字节均为 0x00 | 保留(Reserved) (只供 FAT32 使用)供以后扩充使用的保留空间。本字段的值总为 0 | |||||
FAT32 分区的扩展 BPB 字段 | ||||||||
字节 位移 | 字段 长度 (字节) | 对应取值 | 名称和定义 | |||||
0x40 | 1 | 0x80 | 物理驱动器号( Physical Drive Number) 与 BIOS 物理驱动器号有关。软盘驱动器被标识为 0x00,物理 硬盘被标识为 0x80,而与物理磁盘驱动器无关。一般地,在发出一个 INT 13h BIOS 调用之前设置该值, 具体指定所访问的设备。只有当该设备是一个引导设备时,这个值才有意义 | |||||
0x41 | 1 | 0x00 | 保留(Reserved) FAT32 分区总是将本字段的值设置为 0 | |||||
0x42 | 1 | 0x29 | 扩展引导标签(Extended Boot Signature) 本字段必须要有能被 Windows 2000 所识别的值 0x28 或 0x29 | |||||
0x43 | 4 | 0xBC0C96E0 | 分区序号(Volume Serial Number) 在格式化磁盘时所产生的一个随机序号,它有助于区分磁盘 | |||||
0x47 | 11 | "NO NAME" | 卷标(Volume Label) 本字段只能使用一次,它被用来保存卷标号。现在,卷标被作为一个特殊文件保存在根目录中 | |||||
0x52 | 8 | "FAT32" | 系统 ID(System ID) FAT32文件系统中一般取为"FAT32" |
通过上面Boot Sector表格了解到重要信息总结如下:
- 扇区字节数和每簇扇区数
硬件扇区的大小为512Bytes,每个簇占用扇区数为32个扇区。 - 保留扇区数
我们可以通过保留扇区数可以找到FAT表的起始位置,FAT Table Start Address = 0x092A * 512 = 0x125400 Bytes
。确实在地址0x125400
位置找到了FAT表,如下:(后面还会详细分析FAT表)
- 总扇区数
通过总扇区数可以计算得到SD卡的总容量大小:0x03B723C1 * 512 = 31914951168 Bytes
- 根目录开始地址
根目录开始地址的计算公式如下:
根目录开始地址 = ((保留扇区数) + (每 FAT 扇区数) * (FAT 数)) * 扇区字节数
根目录开始地址 = (0x092A + 0x00003B6B * 0x02) * 512 = 0x1000000 Bytes
5.FAT32根目录(Root folder)
根文件夹描述根分区中的文件和文件夹。FAT32根文件夹结构的结构分为2种:短文件格式和长文件格式。
-
短文件格式
- 对于短文件名,系统将文件名分成两部分进行存储,即主文件名+扩展名。0x0~0x7字节记录文件的主文件名,0x8~0xA记录文件的扩展名,取文件名中的ASCII码值。不记录主文件名与扩展名之间的
"."
,主文件名不足 8 个字符以空白符(20H)填充,扩展名不足 3 个字符同样以空白符(20H)填充。0x00 偏移处的取值若为 00H,表明目录项为空;若为E5H,表明目录项曾被使用,但对应的文件或文件夹已被删除。(这也是误删除后恢复的理论依据)。文件名中的第一个字符若为“.”
或“..”
表示这个簇记录的是一个子目录的目录项。“.”
代表当前目录;“..”
代表上级目录。 - 0xB的属性字段:可以看作系统将 0xB的一个字节分成 8 位,用其中的一位代表某种属性的有或无。如 00000101 就表示这是个文件,属性是只读、系统。
- 0x0E~0x0F文件创建时间字段:
Bit 0~4:为文件创建时间字段秒/2的值;
Bit 5~10:为文件创建时间字段分钟的值;
Bit 11~15:为文件创建时间字段小时的值;
- 0x10~0x11文件创建日期字段:
Bit 0~4:为文件创建日期字段日期数的值;
Bit 5~8:为文件创建日期字段月份的值;
Bit 9~15:为文件创建时间字段年号-1980的值
- 0x14 ~ 0x15和0x1A~0x1B文件起始簇号:它存放文件或目录的表示文件的起始簇号,系统根据掌握的起始簇号在FAT表中找到入口,然后再跟踪簇链直至簇尾,同时用 0x1C~0x1F处字节判定有效性。就可以完全无误的读取文件(目录)了。
- 普通子目录的寻址过程也是通过其父目录中的目录项来指定的,与数据文件(指非目录文件)不同的是目录项偏移 0xB的第 4 位置 1,而数据文件为0。
具体FAT32短文件格式表格如下:
- 对于短文件名,系统将文件名分成两部分进行存储,即主文件名+扩展名。0x0~0x7字节记录文件的主文件名,0x8~0xA记录文件的扩展名,取文件名中的ASCII码值。不记录主文件名与扩展名之间的
-
长文件格式
FAT32 的一个重要的特点是完全支持长文件名。长文件名依然是记录在目录项中的。为了低版本的 OS 或程序能正确读取长文件名文件,系统自动为所有长文件名文件创建了一个对应的短文件名,使 对应数据既可以用长文件名寻址,也可以用短文件名寻址。不支持长文件名的 OS 或程序会忽略它认为不合法的长文件名字段,而支持长文件名的 OS 或程序则会以长文件名为显式项来记录和编辑,并隐藏起短文件名。
当创建一个长文件名文件时,系统会自动加上对应的短文件名,其一般有的原则:- 取长文件名的前 6 个字符加上
"~1"
形成短文件名,扩展名不变。 - 如果已存在这个文件名,则符号
"~"
后的数字递增,直到 5。 - 如果文件名中
"~"
后面的数字达到 5,则短文件名只使用长文件名的前两个字母。通过数学操纵长文件名的剩余字母生成短文件名的后四个字母,然后加后缀"~1"
直到最后。 - 如果存在老 OS 或程序无法读取的字符,换以
"_"
长文件名的实现有赖于目录项偏移为 0xB 的属性字节,当此字节的属性为:只读、隐藏、系统、卷标,即其值为 0FH 时,DOS 和 WIN32 会认为其不合法而忽略其存在。系统将长文件名以 13 个字符为单位进行切割,每一组占据一个目录项。所以可能一个文件需要多个目录项,这时长文件名的 各个目录项按倒序排列在目录表中,以防与其他文件名混淆。长文件名中的字符采用 unicode 形式编码,每个字符占据 2 字节的空间。
- 取长文件名的前 6 个字符加上
NOTE:系统在存储长文件名时,总是先按倒序填充长文件名目录项,然后紧跟其对应的短文件名。从以上的表格可以看出,长文件名中并不存储对应文件的文件开始簇、文件大小、各种时间和日期属性。文件的这些属性还是存放在短文件名目录项中,一个长文件名总是和其相应的短文件名一 一对应,短文件名没有了长文件名还可以读,但长文件名如果没有对应的短文件名,不管什么系统都将忽略其存在。所以短文件名是至关重要的。
下面的截图为SD卡根目录(Root folder)实例解析:
下面通过实例文件夹:System Volume Information
来解析里面的数据,如下:
通过上面实例表格了解到重要信息总结如下:
- 文件名称和属性
文件名:System Volume Information
属性:目录
、系统
、隐藏
- 文件创建时间
文件创建时间的值为0xAA7D
,按照前面介绍的公式解析时间为21:19:58
,解析过程如下:
- 文件创建日期
文件创建时间的值为0x5276
,按照前面介绍的公式解析时间为2021/03/22
,解析过程如下:
- 文件起始簇号
文件起始簇号的值为0x00000003
,说明该文件的起始地址为:
文件地址 = 根目录开始地址 + ((起始簇号 - 根目录簇号(Root Cluster Number)) * 每簇扇区数(Sectors Per Cluster) * 扇区字节数(Bytes Per Sector))
文件地址 = 0x1000000 + ((3 - 2) * 32 * 512) = 0x1004000 Bytes
经过计算可以得到文件起始存放位置在0x1004000
,然后验证果然在该位置找到了文件夹:System Volume Information
存放的数据,如下:
6.FAT32 FAT表
FAT表记录了磁盘数据文件的存储链表,对于数据的读取而言是极其重要的,以至于Microsoft为其开发的FAT文件系统中的FAT表创建了一份备份,就是我们看到的FAT2。FAT2 与FAT1 的内容通常是即时同步的,也就是说如果通过正常的系统读写对FAT1 做了更改,那么FAT2 也同样被更新。
FAT表工作的原理:当存储一个大的文件时,会以簇为单位拆分成多个簇存放在磁盘,它们的存放的簇号并不一定是连续的,所以这里用到了FAT表,将该文件所用到的簇号串联起来。
例如截图如下,绿色的部分为存储文件的数据,FAT表查询顺序如下:
- 从根目录(Root folder)找到该文件的起始簇号 12,并且读取簇号 12地址的文件数据。
- 从FAT表中,找到簇号12存放的下一个簇号为13,并且读取簇号 13地址的文件数据。以此类推到簇号65。
- 从FAT表中,找到簇号65存放的下一个簇号为87,并且读取簇号 87地址的文件数据。下一次FAT表查询直接从簇号65跳转到簇号 87。
- 经过上面的连续查询FAT,直到读取FF标记,代表文件读取完成。
NOTE:以上的分析只是理论上的,和实际使用有一些差异。后面会以实际的例子来介绍FAT表整个的工作过程。
下面通过实例根目录(Root folder)文件:stm32f4xx_gpio.c
来介绍FAT表的使用:
通过上面实例表格了解到重要信息总结如下:
- 文件起始簇号
文件起始簇号为0x0000 000C
,说明该文件的起始地址为:
文件地址 = 根目录开始地址 + ((起始簇号 - 根目录簇号(Root Cluster Number)) * 每簇扇区数(Sectors Per Cluster) * 扇区字节数(Bytes Per Sector))
文件地址 = 0x1000000 + ((0x0C - 2) * 32 * 512) = 0x1028000 Bytes
经过计算可以得到文件起始存放位置在0x1028000
,然后验证果然在该位置找到了文件:stm32f4xx_gpio.c
存放的数据,如下:
- 文件大小
文件大小为0x0000 6011(24593)
,在下面的文件属性截图可以查看到文件大小确实为24593字节
。
???为什么占用磁盘空间为32768字节????
因为系统读写磁盘的单位为簇为单位,存放数据不满足一个簇时也将占用这整个簇(其它文件也无法使用该簇多余的空间),所以这里占用空间32768字节 = 2簇 * 32 * 512 字节 = 32768字节
这样计算得来。
读取完文件起始簇号存放的文件数据后,就查询下一个簇号存放数据。 FAT表以簇为单位,标识分区中空间的使用情况(每个标识占4字节)。FAT前2簇为保留簇(簇0和簇1),不分配给文件使用,其内容含义如下所示:
- FAT16:F8 FF FF FF
- FAT32:F8 FF FF 0F FF FF FF FF
一个FAT表项值表明了文件占用的一个簇号并指明下一簇号的位置,说明读取完簇号0x0C(12)后读下一个簇号0x0D(13),其示意图如下图所示:
FAT表按顺序依次记录了该盘各簇的使用情况,是一种位示图法。每簇的使用情况用32位二进制填写,未被分配的簇相应位置写零;坏簇相应位置填入特定值;已分配的簇相应位置填入非零值,具体为:如果该簇是文件的最后一簇,填入的值为FFFFFF0FH(即0x0FFFFFFF),如果该簇不是文件的最后一簇,填入的值为该文件占用的下一个簇的簇号,这样,正好将文件占用的各簇构成一个簇链,保存在FAT表中。0000000H、00000001H两簇号不使用,其对应的两个DWORD位置(FAT表开头的8个字节)用来存放该盘介质类型编号。
FAT表的大小就由该逻辑盘数据区共有多少簇所决定,取整数个扇区。
当文件系统被创建,也就是进行格式化操作时,分配给FAT区域的空间将会被清空,在FAT1与FAT2的0号表项与1号表项写入特定值。由于创建文件系统的同时也会创建根目录,也就是为根目录分配了一个簇空间,通常为2号簇,所以2号簇所对应的2号FAT表项也会被写入一个结束标记,如上图所示。
如果某个簇存在坏扇区,则整个簇会用FAT表项值0xFFFFFF7标记为坏簇,不再使用,这个坏簇标记就记录在它所对应的FAT表项中。
在文件系统中新建文件时,如果新建的文件只占用一个簇,为其分配的簇对应的FAT表项将会写入结束标记。如果新建的文件不只占用一个簇,则在其所占用的每个簇对应的FAT表项中写入为其分配的下一簇的簇号,在最后一个簇对应的FAT表象中写入结束标记。
新建目录时,只为其分配一个簇的空间,对应的FAT表项中写入结束标记。当目录增大超出一个簇的大小时,将会在空闲空间中继续为其分配一个簇,并在FAT表中为其建立FAT表链以描述它所占用的簇情况。
对文件或目录进行操作时,他们所对应的FAT表项将会被清空,设置为0以表示其所对应的簇处于未分配状态。
7.如何使用WinHex工具查看磁盘数据
我们在分析FAT32文件系统时,需要用到WinHex工具查看磁盘上的原始数据。操作步骤如下:
- 安装WinHex工具
安装完成后的工具图标如下:
- 打开WinHex工具
查看磁盘的原始数据需要以管理员的方式打开。
- 打开磁盘
选择Tools
—>Open Disk
打开磁盘
- 选择需要打开的磁盘
- 查看磁盘数据
选择Partition
,然后鼠标键右击选择Template
可以查看Boot Sector数据。
- 查看FAT32 Boot Sector数据
FAT32 Boot Sector数据详细分析可以查看上面的分析。
- 查看FAT32分区数据
可以鼠标双击Partition
,进入FAT32分区数据。
8.参考资料
-
FAT32文件系统参考资料
下载地址如下:
https://download.csdn.net/download/ZHONGCAI0901/16158151 -
硬盘基本知识(磁头、磁道、扇区、柱面)
https://www.pianshen.com/article/8880114034/ -
FAT32文件系统–For TF卡
https://www.pianshen.com/article/5359401055/ -
Microsoft MSDN FAT File System
https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc778296(v=ws.10)?redirectedfrom=MSDN