FAT32文件和目录的组织方式

 参考资料:分析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部分,来索引出幸存文件了,这就是数据恢复软件的原理。

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值