FAT的全称是文件分配表(File Allocation Table),是Windows操作系统上常见的一种文件系统。FAT文件系统起源于20世纪70年代末80年代初,是由微软公司发明并拥有部分专利的文件系统,用于MS-DOS,也是所有非NT核心的Windows版本使用的文件系统。当时计算机性能有限,因而FAT被设计为一个相对简单的文件系统,用于容量小于500KB的软盘。后来随着计算机硬件处理能力和存储容量的发展,FAT的功能也在不断改进中,目前已经可以支持大容量的存储介质。FAT的发展过程经历了FAT12、FAT16、FAT32三个阶段,这三种文件系统的主要区别在于FAT项占据的位数不同。
FAT文件系统的最初版本是FAT12。FAT12采用12位文件分配表,并因此而得名,在DOS3.0以前使用,可以管理的磁盘分区容量不超过8MB。应该说在当时,这个容量上限还是适用的,但以现在的眼光来看则已经过时了。
早在DOS2.0被广泛应用期间,对更大容量的磁盘管理能力的需求就出现了,所以在DOS3.0版本中微软推出了新的文件系统FAT16。除了采用16位的分区表之外,FAT16和FAT12在其他地方都非常相似。实际上,随着字长增加4位,可以使用的簇的总数增加到了65546。在FAT16中,当簇的总数小于4096的时候,还是使用FAT12的分区表;当需要超过4096的时候才使用FAT16。早期的FAT16文件系统管理磁盘的能力上限是32MB,1987年DOS4.0之后的FAT16可以管理128MB的磁盘。之后这个数字不断扩大,一直到2GB。在整整10年中,2GB的磁盘管理能力都是大大超过实际需要的。
FAT16分区格式存在严重的缺点:大容量磁盘利用效率低。在微软的DOS和Windows系列中,磁盘文件分配以簇为单位,一个簇只能分配给一个文件使用,不管这个文件占用整个簇容量的多少。这样,即使一个很小的文件也要占用一个簇,剩余的空间便全部闲置,造成磁盘空间的浪费。由于分区表容量的限制,FAT16分区创建得越大,磁盘上每个簇的容量也越大,从而造成的浪费也越大。为了解决这个问题,微软推出了一种全新的磁盘分区格式FAT32,并在Windows95OSR2及以后的Windows版本中提供支持。
FAT32采用32位的文件分配表,磁盘的管理能力大大增强,突破了FAT16的2GB分区容量限制。FAT32刚刚推出时,主流硬盘的存储容量并不大,所以微软设计的FAT32可以应用在一个不超过8GB的分区中。FAT32分区格式的每个簇都固定为4KB,与FAT16相比,大大减少了磁盘空间的浪费,提高了磁盘的利用率。
FAT文件系统用“簇”作为数据单元,这意味着一个文件占据存储空间的大小必须是簇的整数倍。簇由一组连续的扇区组成,所含的扇区数必须是2的整数次幂。簇的最大值为64个扇区,即最大的簇大小是32KB。所有簇从2开始进行编号,每个簇都有一个自己的地址编号。之所以以簇为单位而不以扇区为单位进行磁盘的分配,是因为当分区容量较大时,采用大小为512B的扇区管理会增加FAT表的项数,对大文件存取增加消耗,文件系统效率不高。
FAT文件系统有两个重要的数据结构:文件分配表和目录项。其中文件分配表就是英文缩写“FAT”的中文译名。
FAT文件系统的每一个文件和文件夹都被分配到一个目录项,目录项中记录着文件名、大小、文件内容起始地址以及其他一些元数据。
文件和文件夹内容存储在“簇”中。如果一个文件或文件夹需要更多的簇,则用文件分配表(FAT)来描述如何找到另外的簇。文件分配表用于指出文件的下一个簇,同时也说明了簇的分配状态。
目录项结构、簇和文件分配表结构之间的关系如图2-1所示。
图2-1 目录项、簇和文件分配表结构之间的关系
一个文件的起始簇号记录在它的目录项中,该文件的其他簇则用一个簇链结构记录在文件分配表中。如果要寻找文件的下一簇,只需要查看该文件的目录项中描述的起始簇号所对应的文件分配表项,如果该文件只有一个簇,则此处的值为一个结束标记;如果该文件不止一个簇,则此处的值是它的下一个簇的簇号。
在FAT中,有关文件系统自身的一些数据记录在DOS引导扇区(DBR)中。引导扇区位于整个文件系统的0号扇区,属于文件系统“保留扇区”的一部分。DBR中记录着文件系统的起始位置、大小、文件分配表个数及大小等相关信息。FAT12和FAT16的保留扇区通常为1个扇区,因而也就是DBR本身;FAT32的保留扇区则要多一些,通常有32个扇区(DOS系统实现为32个扇区),除0号引导扇区外,还在另一个保留扇区中为其存储了一个DBR备份扇区。
在FAT文件系统中,同时使用扇区地址和簇地址两种地址管理方式。这是因为只有存储用户数据的数据区使用簇进行管理(FAT12和FAT16的根目录除外),所有簇都位于数据区。文件系统其他区域是以扇区进行管理的。
任何文件系统都是用来组织和管理存储介质中的数据的。一般在使用硬盘存储数据之前,首先对其进行分区,然后对分区进行格式化,格式化之后用户才可以创建文件来存储数据。格式化的过程就是在分区内建立文件系统的过程,一个文件系统由系统结构和按一定规则存放的用户数据组成。
第1章我们已经讲到,硬盘由很多盘片(platter)组成,每个盘片的每个面都有一个读写磁头(Heads)。如果有N个盘片,就有2N个面,对应2N个磁头,从0、1、2开始编号。每个盘片被划分成若干个同心圆磁道,这样每个盘片的半径均为固定值R的同心圆在逻辑上形成了一个以电机主轴为轴的柱面(Cylinders),从外至里编号为0、1、2……每个盘片上的每个磁道又被划分为几十个扇区(Sector),通常的容量是512B,并按照一定规则编号为1、2、3……形成Cylinders×Heads×Sector个扇区。这三个参数即是硬盘的物理参数。
所谓磁盘分区,就是使用分区编辑器(partition editor)将磁盘的整个存储空间划分为几个部分,每个部分称为一个分区,不同的分区可以以不同的文件系统来组织数据。在分区时,各分区都不允许跨柱面,即均以柱面为单位,这就是通常所说的分区粒度。硬盘的分区由主分区和扩展分区组成,扩展分区又划分为多个逻辑分区。
主分区:也叫引导分区,最多能创建4个,当创建4个主分区(注意扩展分区也是一个主分区)时,就无法再创建扩展分区了,当然也就没有逻辑分区了。主分区是可以直接使用的分区,系统启动文件只能放到主分区。
扩展分区:除了主分区外,剩余的磁盘空间就是扩展分区了,扩展分区是一个概念,实际上是看不到的。扩展分区是不能直接使用的,使用前需要将它划分为若干个逻辑分区。
逻辑分区:在扩展分区上面可以创建多个逻辑分区。即逻辑分区是扩展分区的一部分。
具有4个分区的磁盘结构如图2-2所示,其中主引导扇区的主要组成和作用如下。
主引导扇区位于磁盘的第一个扇区,即0号扇区,主要由主引导记录(MBR,446字节)、分区表(64字节)、结束标志(2字节)三部分构成,总共占512字节。
计算机在上电完成BIOS自检后,会将该主引导扇区加载到内存中并执行前面446字节的MBR,引导程序的主要作用如下:扫描分区表,找到一个激活(可引导)分区;找到激活分区的起始扇区;将激活分区的引导扇区装载到内存中;将控制权交给引导扇区代码。在一些非启动磁盘上,MBR引导代码可能都是0,这对磁盘使用没有任何影响。
图2-2 具有4个分区的磁盘结构
分区表是磁盘管理中最重要的部分,通过分区表信息可定位各个分区,访问用户数据。每个分区表项占用16字节。这16字节中存有活动状态标志、文件系统标识、起止柱面号、磁头号、扇区号、隐含扇区数目(4字节)、分区总扇区数目(4字节)等内容。由于主引导扇区只有64字节用于分区表,所以只能记录4个分区的信息。这就是硬盘主分区数目不能超过4个的原因。需要注意的是,分区表项的第1字节表示该分区是否是活动分区,即是否包含系统引导扇区,用来引导操作系统。每个磁盘只能同时有一个活动分区,活动分区的引导指示符是0x80,其他均为0x00;尽管我们可以通过一些工具来手动修改引导指示符,但是引导程序只会查找并使用第一个引导指示符为0x80的活动分区。
“55AA”为结束标志,或者称魔数,占主引导扇区最后2字节。每次执行系统引导代码时都会检查主引导扇区最后2字节是否是“55AA”,若是,则继续执行后续的程序,否则认为这是一个无效的引导扇区,停止引导系统。
FAT文件系统分为FAT12、FAT16、FAT32三种类型。这三种文件系统的主要区别在于FAT项占据的位数不同。目前使用最多的是FAT32,所以本书将以FAT32为例详细讲解FAT文件系统。
文件系统安装在磁盘分区上,一个FAT32文件系统结构如图2-3所示。
图2-3 FAT32文件系统结构
保留扇区由引导扇区和其余保留扇区两部分组成。引导扇区也称为DBR(DOS Boot Record)扇区,通常占用磁盘分区在逻辑上的第0扇区,是保留扇区中的第1个扇区,也是文件系统的第1个扇区。这512字节中包含了跳转指令、厂商标志、操作系统版本号、BIOS参数块(BPB)、扩展BPB、操作系统引导程序、结束标志等部分。其中BPB记录着文件系统的参数,包括:每扇区字节数、每簇扇区数、保留扇区数、FAT表项个数、文件系统大小(扇区数)、每个FAT表项大小(扇区数)、根目录起始簇号等。
DBR扇区的结构定义如表2-1所示。
表2-1 DBR扇区结构表
操作系统在启动过程中,MBR将CPU执行转移给DBR,DBR的前三字节是可执行的CPU跳转指令。该指令负责跳过接下来的几个不可执行的字节(BPB和扩展BPB),跳到操作系统引导代码部分,继续启动操作系统。
在FAT32中,保留扇区的个数通常为32。FAT32中的保留扇区除了该分区第0扇区用作DBR、第2扇区(Windows98系统)或第0xC扇区(Windows2000、Windows XP)用作OS引导代码扩展部分外,其余扇区都不参与操作系统管理与磁盘数据管理,通常情况下是没用的,只是为了对DBR作备份或留待以后升级时用。在FAT16及FAT12文件系统中,保留扇区的数据通常设置为1,即仅含DBR扇区。
文件分配表紧随保留扇区之后,共有两个:FAT1和FAT2,其中FAT2是FAT1的备份,即这两个文件分配表通常是一致的,FAT文件系统的名称也是因此而来。对于文件系统来说,FAT有两个重要作用:描述簇的分配状态以及标明文件或目录的下一簇的簇号。
FAT32中簇地址长度为32位,FAT中的所有字节位置以32位(4字节)为单位进行划分,并对所有划分后的位置由0进行地址编号。0号地址与1号地址被系统保留并存储特殊标志内容。从2号地址开始,每个地址对应于数据区的簇号,FAT中的地址编号与数据区中的簇号相同。我们称FAT中的这些地址为FAT表项,FAT表项中记录的值称为FAT表项值。FAT表项值的取值情况如下:
1)当文件系统被创建,也就是进行格式化操作时,分配给FAT区域的空间将会被清空,在FAT1与FAT2的0号表项与1号表项写入特定值。由于创建文件系统的同时也会创建根目录,也就为根目录分配了一个簇空间,通常为2号簇,所以2号簇对应的2号FAT表项也会被写入一个结束标记。
2)如果某个簇未被分配使用,它所对应的FAT表项内的FAT表项值即用0进行填充,表示该FAT表项所对应的簇未被分配。
3)当某个簇已被分配使用时,则它对应的FAT表项内的FAT表项值也就是该文件的下一个存储位置的簇号。如果该文件结束于该簇,则在它的FAT表项中记录的是一个文件结束标记,对于FAT32而言,代表文件结束的FAT表项值为0x0FFFFFFF。
4)如果某个簇存在坏扇区,则整个簇会用FAT表项值0xFFFFFF7标记为坏簇,并不再使用,这个坏簇标记就记录在它所对应的FAT表项中。
5)由于簇号起始于2号,所以FAT表项的0号表项与1号表项不与任何簇对应。FAT32的0号表项值总是“F8FFFF0F”。
6)1号表项可能被用于记录“脏”标志,以说明文件系统没有被正常卸载或者磁盘表面存在错误。不过这个值并不重要。正常情况下1号表项的值为“FFFFFFFF”或“FFFFFF0F”。
7)在文件系统中新建文件时,如果新建的文件只占用一个簇,为其分配的簇对应的FAT表项将会写入结束标记。如果新建的文件不只占用一个簇,则在其所占用的每个簇对应的FAT表项中写入为其分配的下一簇的簇号,在最后一个簇对应的FAT表项中写入结束标记。
8)新建目录时,只为其分配一个簇的空间,对应的FAT表项中写入结束标记。当目录增大超出一个簇的大小时,将会在空闲空间中继续为其分配一个簇,并在FAT中为其建立FAT表链以描述它所占用的簇情况。
9)对文件或目录进行操作时,它们所对应的FAT表项将会被清空,设置为0以表示其所对应的簇处于未分配状态。
数据区是真正用于存放用户数据的区域,文件的内容都存放在数据区。数据区紧跟在FAT2之后,簇是最小分配单位,所有的簇从2开始进行编号。也就是说,2号簇的起始位置就是数据区的起始位置。FAT文件系统的数据存储采用小端(Little Endian)方式。
·根目录
虽然原则上FAT32允许根目录位于数据区的任何位置,但通常情况下它都位于2号簇。要想定位一个FAT32文件系统的数据起始处,可以通过引导扇区的相关参数计算出来。首先从引导扇区的偏移0x0E~0x0F字节处得到保留扇区数;然后从偏移0x10字节处得到FAT的个数;最后从偏移0x24~0x27字节处得到每个FAT的大小扇区数。利用如下公式计算:
数据区起始扇区号=保留扇区数+每个FAT的大小扇区数×FAT个数
要想计算其他已知簇号的扇区号,还要由引导扇区的偏移0x0D字节处查找到每个簇大小扇区数,并使用如下公式计算:
某簇起始扇区号=保留扇区数+每个FAT的大小扇区数×FAT个数+(该簇簇号-2)×每簇扇区数
根目录在文件系统建立时即已被创建,其目的就是存储目录(也称文件夹)或文件的目录项。文件系统刚被创建时还没有存储任何数据,根目录下没有任何内容,文件系统只是为根目录分配了一个簇的空间(通常为2号簇),将结束标记写入该簇对应的FAT表项,表示该簇已经被分配使用。这时候为根目录分配的空间没有任何内容。
不管是根目录还是子目录下的目录项,都具有以下四个基本特性:第一,为文件或目录分配的第一个簇的簇号记录在它的目录项中,其他后续簇则由FAT中的FAT表链进行跟踪。第二,目录项中除记录子目录或文件起始簇号外,还记录它的名称、大小(子目录没有大小)、时间值等信息。第三,每个子目录或文件除具有一个短文件名目录项外,还会有长文件名目录项。短文件名目录项固定占用32字节,长文件名目录项则根据需要占用1个或者若干个32字节。第四,对于同一个子目录或文件,它的长文件名目录项存放在它的短文件名目录项之前,如果长文件名目录项占用多个32字节,则按倒序存放于短文件名目录项之前。
·子目录
在FAT32文件系统中,除根目录在创建文件系统时即被建立并分配空间外,其他所有的子目录都是在使用过程中根据需要建立的。新建一个子目录时,在其父目录中为其建立目录项,在空闲空间中为其分配一个簇并对该簇进行清零操作,同时在为其分配的簇空间开始处建立两个目录项来描述子目录本身及其父目录的信息,以使目录间建立联系。
·目录项
在FAT32文件系统中,根据结构不同可以将目录项大致分为五种:卷标目录项、“.”目录项和“..”目录项、短文件名目录项和长文件名目录项。要找到一个目录项的位置只能用分配给文件或子目录的全名进行搜索。
短文件名目录项:这是最重要的数据结构,其中存放着有关子目录或文件的短文件名、属性、起始簇号、时间值以及内容大小等信息。在FAT32文件系统中,子目录被看作一种特殊的文件。之所以称其为短文件名目录项,是因为它所记录的文件名延续了DOS时代的8.3格式,即8个字符的名字加上3个字符的扩展名,如果文件名不足8个字符,用0x20进行填充;超过8个字符时则会被截短,因为短文件名目录项中没有足够的空间记录超出的部分,截短的方法是取文件名的前6个字符加上“~1”(如果有同名文件,则会依次递增该数值),然后加上其扩展名;如果是子目录,则将扩展名部分用“0x20”进行填充。
为了解决长文件名的问题,FAT文件系统又增加了一种“长文件名”目录项结构。其从Windows95开始,不管文件名的长度是否超过8个字符,都会同时为其创建短文件名目录项和长文件名目录项,因为短文件名不区分大小写,而长文件名则是区分大小写的。
长文件名目录项:FAT32文件系统在为文件分配短文件名目录项的同时会为其分配长文件名目录项。文件系统在为文件创建长文件名(Long File Name,LFN)类型的目录项时,并没有舍弃原有的短文件名目录项,具有LFN的文件同时也有一个常规的SFN(Short File Name,短文件名)类型目录项。之所以仍然需要SFN,是因为LFN目录项只包含文件的名字,而不包括任何有关时间、大小及起始簇号等信息,这些信息仍然需要用SFN目录项来记录。
“.”目录项和“..”目录项:一个子目录的起始簇的前两个目录项为“.”目录项和“..”目录项,子目录通过这两个目录项及其在父目录中的目录项建立起父子目录的联系。“.”目录项位于子目录起始簇的第一个目录项位置,它用以表明该簇是一个子目录的起始簇。另外,该目录项实际上是对目录自身的描述,它记录了该子目录的时间信息、起始簇号等。需要注意的是,它所记录的起始簇号也就是该子目录目前所处的位置。“..”目录项位于子目录起始簇的第二个目录项位置,用于描述该子目录的父目录的相关信息。
卷标目录项:如果创建文件系统时指定了卷标,则会在根目录下第一个目录项的位置建立一个卷标目录项。卷标名最多允许占用11字节,也就是为短文件名分配的11字节的文件名区域,如果卷标名不足11字节,则用0x20填充(由于每个汉字占用2字节空间,而卷标最多允许11字节,所以用汉字命名卷标时,卷标的长度不能超过5个汉字)。卷标目录项结构与普通短文件名目录项结构完全相同,但没有创建时间和访问时间,只有一个最后修改时间。
分区是以柱面为粒度的,如果磁盘总空间不是柱面的整数倍,不够一个柱面的空间就形成了剩余空间,这一部分空间是无法被利用的。
FAT文件系统经过多年的发展和演进,应该说是一个相当成熟的文件系统,其规范标准、可靠稳定,可以支持各种不同大小的存储媒介。相比于EXT,FAT是一种简单的文件系统,被所有版本的Windows操作系统和多种类型的嵌入式设备所支持。
FAT32是FAT文件系统目前的最高版本了,改进了诸多方面的缺点。但由于先天不足,FAT文件系统的一些固有缺陷是很难避免的。FAT文件系统最大的缺点是,如果根目录和FAT所在扇区出了问题,会导致整个文件系统的毁灭;另外FAT是链状的,在读写文件时,如果频繁移动文件指针(读写起点位置),可能导致效率大幅度下降。另外,低版本的FAT文件系统只支持短文件名8.3格式(8字节基本部分和3字节扩展名),高版本的FAT虽然支持了长文件名,但为了与低版本兼容,采用了比较麻烦的处理方式,这导致一些潜在的隐患。例如,如果文件系统的数据完整性受到损坏,则包含长文件名的FAT文件系统的数据恢复将比较困难。