入门级:理解FAT32文件系统(转载翻译)

FAT(File Allocation Table )

这个网页的目的是帮助你理解怎么样在微软FAT32文件系统下取得数据,处理的硬盘的大小通常在500M到几百G之间。FAT是一个相对简单和纯净的文件系统。大多数文件系统都支持FAT,包括Linux和MacOS。所以也是需要访问硬盘的底层固件项目的常用文件系统。FAT16和FAT12是适用于小硬盘的小文件系统。这个网页将只重点介绍FAT32,和简单地比较一下他们的不同之处。

但是,这个网页的内容故意掩盖了很多细节和省略了很多这个文件系统出色的地方,为了使得本文更加简单,令之前没有接触过FAT32文件系统的读者更容易理解。

从何说起呢?重头开始怎么样?

设备上的第一个扇区,叫做主引导记录(Master Boot Record - MBR)。你可以通过读LBA=0这个地址得到。在一些行的项目中,你可以不用担心怎么通过CHS模式读写设备,LBA只是把扇区从0开始按顺序排列起来而已,他会更加简单。所有的IDE设备都支持通过LBA读写扇区。而且,所有的IDE设备的扇区都是512字节。现在Mincrosoft的操作系统提到过使用更大的扇区,但是设备依然是使用每个扇区512个字节,微软只是把多个扇区当成一个扇区来看而已。本文以下的内容都只涉及512字节扇区的LAB地址。

记录在主引导记录(MBR)最开始的446个字节的代码是用来启动电脑的。他后面紧跟着的64个字节是分区表,然后最后两个字节总是0x55和0xAA。一个看看这个扇区是不是主引导记录的方法就是看第一个扇区最后两个字节是不是0x55和0xAA。

图1: 主引导记录(MBR)的第一个扇区

主引导记录(MBR)只能描述4个分区。一个叫“逻辑分区”(extended partitioning)的技术可以允许更多的分区。这种技术经常在多于两个分区时使用。逻辑分区在主引导记录里面看起来就和普通分区一样,只是这个分区的第一个扇区也存放着一个分区表。由于简单起见,我们不介绍逻辑分区的内容(暂时把逻辑分区重新分配格式化掉……)。最常见的方案就是之分出一个分区,把剩下的234分区空出来。

每一个分区记录只有16字节,好消息是在多数情况下你可以忽略其中的很多东西。第5个字节是“类型代码”(Type Code),这个代码告诉我们分区的文件系统是什么。第9到第12个字节包含了LBA格式的分区开始扇区地址。


图2: 16字节的分区入口(partition entry)

你只需要看看每个分区的类型代码是不是0x0B和0x0C(这两个代码都代表FAT32)和LBA格式的开始地址就可以知道这个分区在设备上的位置。

至于“扇区数量”(Number of Sectors)区域制定了扇区的长度,防止你把数据卸载这个扇区之外的地方去了。但是,分区的FAT32文件系统自身就含有文件系统大小的信息,所以这个主引导记录里的“扇区数量”区域是多余的。很多Microsoft的操作系统忽略这个区域值,反而依赖与嵌入在每个分区开头第一个扇区里所记录的信息。(没错,我曾经修改过这个值,虽然是无意的)。Linux会检测“扇区数量”这个值,来适当地防止访问超出分区部分的读写操作。大多数的底层固件程序可能会忽略这个值。

“启动标志”(Boot Flag),“CHS起始”(CHS Begin),和“CHS结束”(CHS End)这3个区域我们忽略他们(用LBA代替了CHS)。

FAT32卷ID(Volume ID),另一个“第一扇区”

读一个FAT32文件系统的第一步是读他的第一个扇区,叫卷ID(Volume ID)。卷ID是用从分区表中LBA起始(LBA Begin)区域中的地址找到的。从这节起,你将会提取一些信息,这些信息会告诉你文件在FAT32文件系统中文件的分布。

你可能会找到Microsoft的“描述列表”(specification lists)。FAT32的卷ID(Volume ID)和版本较老的FAT16、FAT12比起来有些小地方不一样。幸运的是,如果只是编写些功能简单的代码,并不需要用到这么多信息。只有4个变量是必须的,以及另外3个用于确认里面的值是否是对应的值。

图4: FAT32卷ID, 重要区域


区域


微软的表述


偏移地址


大小



Bytes Per Sector


BPB_BytsPerSec


0x0B


16 Bits


Always 512 Bytes


Sectors Per Cluster


BPB_SecPerClus


0x0D


8 Bits


1,2,4,8,16,32,64,128


Number of Reserved Sectors


BPB_RsvdSecCnt


0x0E


16 Bits


Usually 0x20


Number of FATs


BPB_NumFATs


0x10


8 Bits


Always 2


Sectors Per FAT


BPB_FATSz32


0x24


32 Bits


Depends on disk size


Root Directory First Cluster


BPB_RootClus


0x2C


32 Bits


Usually 0x00000002


Signature


(none)


0x1FE


16 Bits


Always 0xAA55

原表格:


Field


Microsoft‘s Name


Offset


Size


Value


Bytes Per Sector


BPB_BytsPerSec


0x0B


16 Bits


Always 512 Bytes


Sectors Per Cluster


BPB_SecPerClus


0x0D


8 Bits


1,2,4,8,16,32,64,128


Number of Reserved Sectors


BPB_RsvdSecCnt


0x0E


16 Bits


Usually 0x20


Number of FATs


BPB_NumFATs


0x10


8 Bits


Always 2


Sectors Per FAT


BPB_FATSz32


0x24


32 Bits


Depends on disk size


Root Directory First Cluster


BPB_RootClus


0x2C


32 Bits


Usually 0x00000002


Signature


(none)


0x1FE


16 Bits


Always 0xAA55

在检查三个区域,确认文件系统是使用512字节扇区,2个FAT,和有正确标志(signature)之后,或许你会想把这3个从主引导区的变量值和卷ID归纳成四个简单的数字。这个数字拥有访问FAT32文件系统所需的必要信息。

下面是用C语言写的公式:

1 (unsigned long)fat_begin_lba = Partition_LBA_Begin + Number_of_Reserved_Sectors;
2
3 (unsigned long)cluster_begin_lba = Partition_LBA_Begin + Number_of_Reserved_Sectors + (Number_of_FATs * Sectors_Per_FAT);
4
5 (unsigned char)sectors_per_cluster = BPB_SecPerClus;
6
7 (unsigned long)root_dir_first_cluster = BPB_RootClus;

就像这样,需要的信息大多数时候只是指明第一个簇的位置和FAT(文档分配表)。你需要记住簇的大小,以及根文件夹(Root Directory)在哪里(它的地址),而其他信息经常是不需要用到的(至少对于简单的读取文件操作来说不需要)。

如果你那这些信息与Microsoft的描述信息相比较的话,你应该注意到两个不同的地方。他们缺少了“RootDirSectors”(根文件夹扇区),因为FAT32储存文件夹和子文件夹的方式与储存文件的方式是一样的,所以在FAT32中“RootDirSectors”(根文件夹扇区)总是0。而FAT16和FAT12需要经过额外的步骤来计算出根文件夹(Root Directory)的特定地址。

Microsoft的公式中没有给出“Partition_LAB_Begin”这一项。他们的这些公式都依赖于文件系统开头的部分,不过这点他们没有很明确地说出来而已。你需要在主引导记录中找到“Partition_LAB_Begin”这一项,然后计算出IDE接口正确的LBA地址。因为设备是从LBA=0的MBR开始计算,而不是从卷ID开始计算的。没有加上Partition_LBA_Begin(分区起始地址)是开发者经常犯的错误。所以,特别当你在使用Microsoft的描述方式时,不要忘记加上正确的LBA地址。

本文剩下的部分将会经常提到“fat_begin_lba”,“cluster_begin_lba”,“sectors_per_cluser”,和“root_dir_first_cluster”,而不是主引导记录(MBR)中和卷ID(Volume ID)中的特定的区域。原因是,你不再需要主引导记录(MBR)中和卷ID(Volume ID)中的细节,能最简单地使用这些数值来计算。

FAT32文件系统是怎样排列的?

FAT32文件系统的布局非常简单。第一个扇区总是卷ID(Volume ID)。在中和卷ID之后总是跟着一些未被使用的保留扇区(reserved sectors)。在保留扇区后面是两个FAT(File Allocation Table,文件分配表)。剩下的区域被文件系统以“簇”为单位分配,在最后的簇后面还可能会出现一些未使用的区域。


图5: FAT32文件系统整体布局(一个分区)

这些用来存放所有文件和文件夹的“簇”占据着硬盘大部分的空间。簇是从2号开始的,所以没有0号和1号簇。如果你需要访问特定的簇,你需要用以下这条公式吧簇号(cluster number)转换为IDE设备的LBA地址:

lba_addr = cluster_begin_lba + (cluster_number-2) * sectors_per_cluster;

通常,簇的大小最少为4K(8个扇区),8K,16K和32K的簇很少使用。一些新版本的Microsoft Windows系统允许使用更大的簇,而处于对效率的考虑,扇区(簇?)的大小都是512字节的倍数。Microsoft指出,FAT32描述的文件系统最大的簇大小为32K。

现在只要我们知道文件在哪里……

刚开始时,你只知道第一个簇和根目录(Root directory)的地址。读取目录将可以知道其他文件和目录的名字和起始簇在哪里。很重要的一点是:目录仅仅告诉你怎么找到文件和子目录的起始的簇在哪里。你还需要从目录中获取诸如文件长度、修改时间、文件属性等其他种类的信息。但是目录只会告诉你文件在哪里。要得到起始簇以外的信息,你需要用到FAT(文件分配表)。但首先你需要找到这些文件是从哪里开始的。

在这节里面,我们只会简短地看看目录,知道哪些是找到文件所必须的。然后我们将看看怎么通过文件分配表(FAT)来找到剩下的文件。最后我们将来回头看看关于目录结构的细节。

目录数据被组织成32个字节的记录。这很好,因为每一个扇区刚好可以保存16条记录,没有记录会卡在扇区与扇区中间。这里有四种32个字节的目录记录:

  1. 短文件名的普通记录 - 属性是普通
  2. 长文件名文本 - 属性含有全部的4个字节
  3. 未使用 - 第一个字节为0xE5
  4. 目录结尾 - 第一个字节为0x00

未使用的目录记录可能是被删除的文件的记录,它的第一个字节被0xE5覆盖掉,当有新文件被创建时就可以重新使用这条记录。目录的最后是一条开头第一个字节为0的记录。所有其他的记录都是以非0开头的,所以可以很容易的察觉到你什么时候到达了目录的结尾。

凡是不是以0x00和0xE5开头的记录都是目录的数据,这些记录的格式可以通过检查它的属性字(Attrib byte)来判定。现在,我们将只考虑“旧8.3短文件名格式”(old 8.3 format)的一般目录记录。在FAT32中,所有的文件盒子目录都有短文件名(short name),即使用户赋予了这个文件一个长文件名。所以你不需要解释长文件名记录就可以访问到所有的文件(当你的代码简单到需要忽略他们)。这是一般目录记录的格式:


图6: 32字节目录结构,短文件名格式


区域


Microsoft的描述


偏移地址


地址


Short Filename


DIR_Name


0x00


11 Bytes


Attrib Byte


DIR_Attr


0x0B


8 Bits


First Cluster High


DIR_FstClusHI


0x14


16 Bits


First Cluster Low


DIR_FstClusLO


0x1A


16 Bits


File Size


DIR_FileSize


0x1C


32 Bits

属性字(Attrib byte)通过6bits来决定,具体如下表所示。大多数程序框架都是通过检查属性字来决定这32个字节是一般目录记录还是长文件名数据,以及决定他是一个普通文件还是子目录。长文件名记录会把最后4位置为1。普通的文件很少会把这最后四个位全部置为1。


Attrib Bit


功能


长文件名记录时


注释


0 (LSB)


Read Only


1


不应该位可写入


1


Hidden


1


不应该在目录中显示


2


System


1


是系统文件


3


Volume ID


1


文件名是卷ID(Volume ID)


4


Directory


x


是一条子目录记录


5


Archive


x


在最后一次备份后已经被修改过


6


Ununsed


0


此位应该常为0


7 (MSB)


Ununsed


0


此位应该常为0

剩下的区域就相对简单和直接。开头11字节是短文件名(旧8.3格式-old 8.3 format)。扩展功能允许使用其后面的3个字节。如果文件名的长度小于8字节,剩下的字节就会用0x20填充。起始簇号码被分为两个16位来储存,以及最后4个字节用来储存文件的大小。这两者的储存都是通过字节来存取,低位在前,高位在后。起始簇号码告诉我们文件的数据从硬盘的哪里开始,文件大小告诉我们这个文件有多长。由于硬盘的实际空间都是通过整数个簇来分配的,所以文件大小会让你知道其后的多少个簇存放着文件的数据。

文件分配表(File Allocation Table) - 连成链的簇

目录的入口告诉你文件或子目录的起始簇在硬盘的什么地方,然后就理所当然的从卷ID中的根目录中找到了第一个簇。对于小文件和目录(大小在一个簇以内),你能从文件分配表(FAT)得到的信息只是:这个文件不占用更多的簇。要访问剩下超过一个簇的文件时,你就需要使用文件分配表(FAT)。FAT32这个名字所代表的含义是表格中每一个入口(entry)都是32位的。在FAT16和FAT12中,入口时16和12位的。FAT16和FAT12的工作方式与FAT32相同(只是令人讨厌的是12位的入口经常对齐不了扇区的边界,但是16和32位的入口是不会超出扇区边界的)。我们将只介绍FAT32。

文件分配表是一个很大的32位整数序列。这个序列中的每一个入口(32位)在序列中的位置对应簇的号码,而每个入口中的内容储存着的是该文件下一个簇的位置。文件分配表的目的就是在你知道目前簇的位置时,告诉你下一个簇的位置在哪里。每一个扇区中包含有128个这种32位的整数,所以可以相对简单地知道下一个簇的位置。当前所在的入口中,第7到31位告诉你下一个入口在FAT中的那个扇区,剩下的0到6位告诉你下一个入口是这个扇区中128个入口的哪一个(如果所有位都置为1,说明这是文件的结尾)。

这是一个虚拟的例子,里面有三个小文件和根目录,他们都分布在第一个簇的附近(下图中,文件需要的所有簇号码画在FAT图的旁边)。注意到,下面这个根目录占用了5个簇,但是却悲剧的仅仅含有三个文件。这里只是想展示一下怎么样链式的寻找到簇。但是值得注意的是,如果是小的根目录(只占一个簇),表中第2个入口中的内容将是“FFFFFFFF”(在卷ID中包含了根目录簇的首地址),而不是通过“00000009”号到达其他的簇。然而,当根目录有很多文件的情况下,根目录会占用几个簇,但是这几个簇很少会连在一起,毕竟在你存入更多的文件之前,这个根目录还是一个只占用一个簇的小目录。那么在记住这些要点后,我们来看看简单的例子:

图7: FAT32扇区,根目录和三个文件的簇链

在这个例子中,通过卷ID的信息我们知道根目录从2号簇开始。在FAT的第2个位置里面的数字式9,所以下一个是9号簇。像这样,簇A,B和11保存了根目录剩下的数据。在位置11的数字“FFFFFFFF”表示这里是根目录的最后一个簇。但是你的代码可能不会去读地11个位置,因为你将会找到一个目录结束标记(end-of-directory marker)(开头32个字节全为0)。就这样,这个文件系统就含有大小为一个簇的小型根目录,而且在读取目录时不需要使用FAT,因为只有一个簇。但是如果你在这个簇中没有发现目录结束标记,那么就必须通过FAT来寻找剩下的簇了。

同样的,这里显示了三个文件。在所有情况下,FAT都没有指出哪个是文件的第一个簇,所以必须从目录中提取文件的首地址,然后通过FAT来访问剩下的簇。分配给文件的簇的总字节数必须大于目录记录中文件大小区域指出的文件大小。除非文件的大小不是簇的整数倍的话,否则最后一个簇的末尾就会有一部分未使用的空间。文件大小是0的文件将不会分配簇,在目录记录中的簇数量应该为0。文件大小只占用一个簇的文件在FAT中对应簇的内容为“FFFFFFFF”表示已经到文件末尾。

便捷提示:一个能使代码尽可能简单的方法是是的根目录非常小(少量文件,只用8.3短文件名),避免出现子目录,以及在电脑上运行整理磁盘碎片程序。这样,根目录能在一个簇中找到,然后每个文件占据着地址连续的簇。虽然这种实现的功能非常有限,但是这样你就可以不使用文件分配表了。

更具Microsoft的描述,簇号码其实只占28位,高四位是保留的。你可以在把这些保留的位清零。同样的,文件结束号码(end-of-file number)实际上是所有比0xFFFFFFF8大的数,但是实际上通常使用0xFFFFFFFF。FAT中为全0的标记是自由的位置。同样的,请记住在内存中簇号码是先储存他们的低字节(低地址对低地址)(图7中使用的格式是为了便于我们阅读,但是他们实际上市先储存低字节的-stored LSB first)。所以其实在内存中,FAT中二号簇实际上在二进制编码器中会写成“90000000”,低字节在前。

好消息是,对于固件开发者来说使用FAT是很简单的。但是,FAT的简单导致的大量计算往往也是起作为一个通用文件系统的弱点。例如,为了向文件增加数据,文件系统必须读便整个簇链。在文件中任意位置中定位也需要大量的读取FAT。处于比较的目的,Unix文件系统使用树状结构。其中的簇(也叫作“区块”)会拥有完整的区块列表(在unix哲学中被叫做“inode”)或者拥有另一个包含着inode的区块。制这种方式下,定位一个硬盘中的任意一个区块只需要读取1,2或者3个inode。FAT的另外可以通过缓存来缓解的缺点在于,它数据的物理分布会集中在磁盘的“开头”,而不是分布式储存的。Unix基于inode(inode-based)的文件系统会把inode区块均匀地分布在数据区块之间,然后把数据区块定位在inode区块附近。

关于目录和长文件名的细节

原作者就写到这里了,剩下的这些部分就要靠读者们写了。

翻译原文:https://www.pjrc.com/tech/8051/ide/fat32.html

转载本文请保留以下网址:http://www.cnblogs.com/warren-wong/p/3977685.html

如果发现文中有错误之处,请务必告诉我,谢谢大家。

时间: 2024-10-31 01:55:52

入门级:理解FAT32文件系统(转载翻译)的相关文章

FAT32文件系统学习(1) —— BPB的理解

FAT 32 文件系统学习 1.本文的目标 本文将通过实际读取一个FAT32格式的U盘来简单了解和学习FAT32文件系统的格式.虽然目前windwos操作系统的主流文件系统格式是NTFS,但是FAT32由于其兼容性原因,还是有一定的学习价值.为了能做出一个窗体程序提供直观的感觉,本文的代码采用c#编写,对应的c++代码也会附上. 2.本文目录 1.本文的目标 2.什么是FAT32 3.引导区 2.什么是FAT32 FAT32是Windwos系统硬盘格式分区的一种.这种格式采用32位的文件分配表,

【转载】FAT32文件系统详解

硬盘是用来存储数据的,为了使用和管理方便,这些数据以文件的形式存储在硬盘上.任何操作系统都有自己的文件管理系统,不同的文件系统又有各自不同的逻辑组织方式.例如:常见的文件系统有FAT,NTFS,EXT,UFS,HFS+等等.作者后面的文章会一一讲到,下面就来学习一下基于Windows的FAT32文件系统. FAT32文件系统由DBR及其保留扇区,FAT1,FAT2 和 DATA 四个部分组成,其机构如下图: 这些结构是在分区被格式化时创建出来的,含义解释如下: DBR及其保留扇区:DBR的含义是

FAT32文件系统学习(3) —— 数据区(DATA区)

FAT32文件系统学习(3) —— 数据区(DATA区) 今天继续学习FAT32文件系统的数据区部分(Data区).其实这一篇应该是最有意思的,我们可以通过在U盘内放入一些文件,然后在程序中读取出来:反过来也可以用程序在U盘内写入一下数据,然后在windows下可以看到写入的文件.这些笔者都会在这篇文章中演示.同时,在写这篇文章的时候笔者也发现了许多意想不到的规律. 1.本文目录 1.读取根目录 2.短文件名目录项 3.长文件名目录项 4.U盘写入文件夹 5.参考文献 2.读取根目录 两张FAT

FAT32文件系统学习(2) —— FAT表

1.题外话 在继续本文学习FAT32文件系统之前,先来插入一点别的话题.我们都知道U盘有一个属性是容量,就拿笔者的U盘为例,笔者手上的U盘是金士顿的DataTraveler G3 4GB的一个U盘.电脑上显示的容量如图1所示为3.75GB.那么这个3.75GB是怎么计算出来的呢? 图 1 系统显示U盘属性 我们先来回顾一下上一篇BPB参数当中的Sectors(扇区总数)这个参数,这一参数代表了这个U盘在出厂时的总扇区数,笔者手上这个是7884672个,可以从图2中看到.其中每个扇区为512 B,

Linux系统挂载NTFS文件系统(转载)

转自:http://hermesbox.blogbus.com/logs/47386987.html 今天尝试并成功的将一块500G的移动硬盘挂载到了RHEL5的系统上,甚感欣慰.想到也许以后自己或其他同学们会有类似经历,于是尽量细致的记录于此.     无论是一块安装了Windows/Linux双系统的硬盘,还是通过USB连接的移动硬盘/U盘,都是可以挂载到Linux系统中的.不过由于Windows本身常用的文件系统包括fat32和NTFS,因此还是需要区别的.废话少说,进入正题. 系统环境如

转载翻译文章:JavaScript Module Pattern: In-Dept

# JavaScript Module Pattern: In-Depth # 转载翻译文章:JavaScript Module Pattern: In-Depth*原文*:http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html 模块模式是一种常见的js开发编码模式.通常来说,它很好理解,但还有一些高级应用并没有得到太多关注.这篇文章,我将回顾基础知识,并介绍一些真正了不起的高级主题,其中包括一个我认为极为原始的

FAT32文件系统--For TF卡

1. TF卡空间是如何分配的? 下面以4GB TF卡为例,通过WinHex工具进行分析,其空间分配如下图所示: FAT32把目录当做文件来管理,所以没有独立的目录区,所有的文件目录项都是在数据区里面的. 2. 启动扇区 (DBR) DBR(DOS BOOT RECORD,DOS引导记录),位于柱面0,磁头1,扇区1,即逻辑扇区0 ;    DBR包括: •  一个引导程序: DOS 引导程序完成DOS系统文件(IO.SYS,MSDOS.SYS)的定位与装载 • 一个BPB:  BPB用来描述本D

SD卡FAT32文件系统格式

一.SD卡FAT32文件系统 二.DBR(DOS BOOT RECORD,DOS引导记录) 1.DBR [1]0x00~0x02:3字节,"EB5890",跳转指令. [2]0x03~0x0A:8字节,文件系统标志和版本号,这里为MSDOS5.0. [3]0x0B~0x0C:2字节,每扇区字节数,512(0X02 00). [4]0x0D~0x0D:1字节,每簇扇区数,8(0x08),这个值不能为0,而且必须是2的整数次方,比如1.2.4.8.16.32.64.128. [5]0x0E

FAT32文件系统的存储组织结构(二)

前面已经基于一个格式化的空U盘分析了一下FAT32文件系统存储的组织结构,下面我们从文件操作的角度来分析一下文件系统的运作机制.由于换了个U盘,所以仍然贴出刚格式化的空U盘的几个重要的数据区如下:   我们可以看出,在分区格式化的时候,系统将卷标TEST_FAT32存储在2号簇,即跟目录区,如上面根目录贴图所示.同时,在FDT区2号簇标记位置写入了文件结束符FF FF FF 0F.显然,FAT32文件系统将目录当做普通文件来处理的. 下面我们在根目录下新建一个文件夹TEST1,看会有什么变化: