admin管理员组文章数量:1650764
参考资料:分析FAT32内部结构-入门篇- - RYZZ - 博客园 这篇文章写得不错,我在此基础上加入了一些自己的理解。
目录
1、簇
2、FAT32总体结构
2、DBR区域的结构
3、FAT(File Allocation Table)
4、目录的存储
5、文件的存储
6、文件系统的操作接口
7、写文件时突然断电的后果
文件在硬盘、SD卡等存储,而里面存储的都是二进制数据,驱动程序是如何从这堆二进制数据中,解析出目录层次、文件列表、文件内容的呢?这就必须要了知道FAT32文件系统在存储介质中的组织方式了。所谓组织方式,就是一种约定或规定,大家都遵守这套规则,就可以互相识别(读写)。
1、簇
“簇”是以前的名字,现在叫“分配单元”。但格式化一个U盘或硬盘分区时,会看到下图有个分配单元大小,就是这个东西。他是一个逻辑概念,代表了文件系统的最小存储单位。例如下图是4096字节=4K,你如果要存储一个1kb的文件,他也会占用一个4K的存储单元,后面3k都是浪费的。显然格式化时,把存储单元设置的更大,将有利于大文件的读写速度,但是也会造成存储小文件时的空间浪费,利弊共存,根据你的个人需求合理设置即可。
如下图所示,查看一个小文件的属性,其大小只有167字节,但是占用空间却是4K=4096。占用空间总是“分配单元”的整数倍。
2、FAT32总体结构
FAT32文件系统中的内容共分4个部分如下图
DBR(DOS BOOT RECORD):该分区的引导程序,在DBR的结尾部分会有一些重要的保留扇区(这些保留扇区属于DBR,图中未画出)
FAT1:FAT的首要文件分配表
FAT2:文件分配表的备份
DATA:数据区(最小单位为簇(cluster),一般2个扇区为1簇,是微软规定的一种磁盘存储单位,与Linux的block概念类似)。所谓的存储单元,就在这里了。
2、DBR区域的结构
FAT32的DBR结构图:
红色:跳转指令,将当前执行流程跳转到引导程序处,占2字节,对应汇编JUMP 58H; NOP;
蓝色:OEM代号,由创建该文件系统的厂商规定,占8字节,一般为”MSDOS5.0”
绿色:BPB(BIOS Paramter Block),从DBR的第12个字节开始共占用79字节,记录了文件系统的重要信息,相关字段参数见下表
粉红色:DBR引导程序,如果该分区没安装操作系统那么这段程序是没用的。
黄色:DBR结束标记
BPB表: | ||
偏移 | 字节 | 含义 |
BH | 2 | 每扇区的字节个数 |
DH | 1 | 每簇扇区数 |
EH | 2 | 保留的扇区个数 |
10H | 1 | FAT个数 |
11H | 2 | 不使用(根目录数量,FAT32已突破此限制,已无效,一般为0) |
13H | 2 | 不使用(扇区总数,小于32M时才使用) |
15H | 1 | 存储介质描述符 |
16H | 2 | 不使用(FAT占的扇区数,小于32M时才使用) |
18H | 2 | 每磁道扇区个数 |
1AH | 2 | 磁头数 |
1CH | 4 | 隐藏扇区 |
20H | 4 | 扇区总数(大于32M时使用) |
24H | 4 | FAT占的扇区数(大于32M时使用) |
28H | 2 | 扩展标记 |
2AH | 2 | 版本,一般为0 |
2CH | 4 | 根目录的首簇号 |
30H | 2 | 文件系统整体信息扇区号 |
32H | 2 | DBR备份所在的扇区号 |
34H | 12 | 保留,固定为0 |
40H | 1 | BIOS驱动器号 |
41H | 1 | 不使用,一般为0 |
42H | 1 | 扩展引导标记 |
43H | 4 | 卷序列号 |
47H | 11 | 卷标 |
52H | 8 | 文件系统类型名,固定为”FAT32 ” |
FAT32文件系统在DBR的保留扇区中有一个文件系统信息扇区,用以记录数据区中空闲簇的数量及下一个空闲簇的簇号,该扇区一般在分区的LAB1扇区,也就是紧跟着DBR后的一个扇区,其内如下:
褐色:扩展引导标签,为52 52 61 41,ASCII为”RRaA”
青色:文件系统信息签名,为72 72 41 61,ASCII为”rrAa”
蓝色:空闲簇的数量,(1FBB0)=129968,每个簇1K,约等于127MB,即D盘的大小
紫色:下一个空闲簇的簇号
黄色:结束标记
其他字节:不使用,填充0
3、FAT(File Allocation Table)
文件分配表。DBR区域中存储着FAT的地址信息,根据这个地址我们就可以索引到FAT的内容了。FAT这块内容的大小跟存储介质的总容量和分配单元大小有关。
FAT中的内容是什么呢?光看“文件分配表”这个名字,让人摸不着头脑。其实,里面存储的是每个存储单元链接信息。FAT的内容我们将按4字节来解析,换句话说,我们可以把FAT区域看成一个uint32 [N]数组,数组的每个成员称为一个“表项”,
表:FAT表项内容的意义
0x0000 0000 | 空闲簇,可用簇 |
0x0000 0001 | 保留簇 |
0x0000 0002 ~ 0x0FFF FFEF | 该簇已用,其值指向下一个簇号 |
0x0FFF FFF0 ~ 0X0FFF FFF6 | 这些值保留,不使用 |
0x0FFF FFF7 | 坏簇,当一个簇中有一个扇区损坏(如物理损坏、病毒感染)时称为坏簇,这个簇将不被FAT32使用 |
0x0FFF FFF8 ~ 0x0FFF FFFF | 文件的最后一个簇 |
FAT的前2个元素,第0、第1表项是个固定值=0x0F FF FF F8。如下图所示。从表项2开始,它的值就代表了DATA区对应的存储单元的链接信息了。例如下图的F[2]=0x0F FF FF FF,代表DATA区的2号存储单元后续没有链接到下一个单元。如果F[2]=0x00 00 00 03,则代表2号存储单元的下一个存储单元是3号存储单元。显然,对于一个大文件,就是用这种链式方式存起来的,而且可以实现文件的不连续存储。可以预见,当我们不断地存储文件、删除文件时,会使得存储单元被占用、释放、占用、释放。。时间长了,从整个存储介质上来看,空闲的、占用的存储单元随机的零散的分布在整个介质上,以后新存的文件在U盘中就是支离破碎 不连续的。驱动程序在寻找空闲存储单元时会非常耗时,这就是windows提供的“磁盘整理程序”要解决的问题。
显然,FAT的大小取决于存储介质的总容量和分配单元大小。例如一个8G的U盘,分配单元设为4K,那么整个U盘会被分配为8G/4K=2'000'000个存储单元,也即FAT表的大小为2000000≈2M。
4、目录的存储
目录又叫文件夹,文件夹本质上也是一个文件,只是这个文件里的内容是个文件信息列表而已。注意,这个文件信息列表的格式也已经被FAT32给约定好了,这个列表可不是一个简单的txt。从程序员角度来看,它的内容其实是一个结构体数组。这个数组的每一个成员都是一个结构体,每个结构体描述了一个对应文件的信息。这个结构体结构如下:
字节偏移 | 字节数 | 说明 | |
0H | 8 | 文件名 | |
8H | 3 | 后缀名,扩展名,类型名 | |
BH | 1 | 文件属性 | 0000 0000B, 0H 读写 |
0000 0001B, 1H 只读 | |||
0000 0010B, 2H 隐藏 | |||
0000 0100B, 4H 系统 | |||
0000 1000B, 8H 卷标 | |||
0001 0000B, 10H 子目录 | |||
0010 0000B, 20H 归档 | |||
CH | 1 | 保留 | |
DH | 1 | 创建时间的10毫秒位 | |
EH | 2 | 创建时间 | |
10H | 2 | 创建日期 | |
12H | 2 | 最后一次访问的日期 | |
14H | 2 | 起始簇号的高16位 | |
16H | 2 | 最近一次修改的时间 | |
18H | 2 | 最近一次修改的日期 | |
1AH | 2 | 起始簇号的低16位 | |
1CH | 4 | 文件长度 |
我们把第2个存储单元的内容读出来(如果第2个存储单元后面还有链接的后续单元,),也就得到了根目录下的文件和子目录信息。
下面我们根据一个实例,来看一下如何获取根目录下的所有文件和文件夹信息。
一般来说,根目录的内容被起始存储在第2个存储单元上,首先我们读取FAT的F[2],并根据F[2]的内容把下一个链接的存储单元找到。。依次类推,直到下一个存储单元为0x0FFF FFF8 ~ 0x0FFF FFFF为止。假设我们找到的链表依次为:2->3->100->5->0x0FFF FFFF,这样我们就知道了,根目录的内容被依次存储在了第2、3、100、5个存储单元中。接下来我们按照链表顺序依次读出这4个存储单元的内容即可,然后把他们都读到内存里。
然后用结构体指针指向这块内存,就可以轻松读出根目录下的所有文件和子目录信息,包括文件名、文件夹名、创建时间、文件大小。文件的起始存储单元编号等。
5、文件的存储
与上面目录的存储如出一辙,一模一样,因为目录也是文件,只是是一个内容比较特殊的文件而已。
6、文件系统的操作接口
fopen,其代码的底层本质,就是找到这个文件所在的目录文件(以备修改文件信息)、找到文件内容所在的分配单元编号(也就等价于找到了它在介质中的物理地址),找到了地址就能做读写删等等各种对内容的操作了。
fseek(n),其功能就是找到文件内容第n字节所在的物理地址,显然这个过程不是一个字节一个字节搜索过去的,而是先根据分配单元大小N,计算第n字节在分配单元链表的哪个位置,然后在最后一个分配单元中找这个字节。例如单元大小=4K,n=10002,那么10002除以4096,得到:商2余1810,也即第10002这个字节存储在了本文件的第(2+1)个存储单元的第1810地址处。可见,这个seek过程还是比较快的,耗时主要耗在存储单元的链表遍历上(从而避免了按字节遍历),时间复杂度是O(n)。不过如果驱动程序写的比较优秀的话,可以把这个过程优化成O(1),思路是这样的,在fopen打开文件时,把这个文件的存储单元的链表直接载入到内存数组中,这样就不用遍历了,直接用数组索引就能找到第(商+1)个存储单元的地址。不知道windows里面是不是这么干的。不过这样也有副作用,fopen的时间就会变长了。
7、写文件时突然断电的后果
这种主要发生在突然拔掉U盘、台式机突然断电等情况下。对于拔U盘这种,如果没有发生读写操作,直接拔是不会损坏U盘文件的,那么何时会发生文件读写?一个是电脑读取U盘时,驱动程序会修改文件的访问时间,这等同于写U盘,向U盘写入文件时,自不必说,一定发生了U盘写入。U盘写入时断电,造成的后果,要看断电时刻,驱动程序在写什么。如果是在写文件内容时断了,那最多就是这个文件写入失败,重新插拔U盘,显示出的内容还是之前的样子。如果在修改文件信息时断电,这后果就严重了,大多存储介质不支持单字节读写(单片机、嵌入式程序员应该很清楚这一点),尤其是U盘这种flash闪存设备,要写入或修改前,必先擦除一整个page,可能刚擦完,就断电了。这个page里面存的是目录信息,那么整个目录就丢失了。如果在修改FAT表时断电,那可能整个盘就凉了,在写入文件时,如果发生了跨单元写入,必然涉及到FAT表的修改,如果新的单元对应的表项是0xFF FF FF FF还好,如果不是,那又会触发整个page的擦除(擦除的本质上就是把这个page全烧成FF,这是flash的物理特性, 0xFFFFFFFF等价于已擦除,可以直接写入),这时会导致FAT表的损坏,会丢失很多文件。如果恰好触发了根目录所在page的擦除,那基本上整个盘就凉了,这时只能用数据恢复软件从未被擦除的PAGE中寻找未损坏的FAT部分,来索引出幸存文件了,这就是数据恢复软件的原理。
版权声明:本文标题:FAT32文件和目录的组织方式 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/xitong/1729529830a1204759.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论