FAT文件系统详解

1. 引言

FAT(File Allocation Table,文件分配表)文件系统是计算机历史上最具影响力的文件系统之一。自1980年代诞生以来,它凭借简单、兼容性强的特点,成为跨平台存储的"通用语言"。无论是早期的MS-DOS、Windows系统,还是如今的U盘、SD卡、数码相机,FAT文件系统都扮演着重要角色。

本文将深入剖析FAT文件系统的内部结构、工作原理、各版本差异,以及它在现代存储设备中的应用场景,帮助读者全面理解这一经典文件系统。

2. FAT文件系统的发展历史

2.1 诞生背景

1980年,微软为QDOS(Quick and Dirty Operating System)开发了FAT文件系统,随后被用于IBM PC上的MS-DOS。当时磁盘容量极小(通常只有几百KB),FAT的设计目标就是简单高效地管理这些小容量存储介质。

2.2 版本演进

FAT文件系统经历了多个版本的演进:

版本 推出时间 最大分区容量 主要应用
FAT12 1980年 32MB 软盘、早期MS-DOS
FAT16 1987年 2GB 早期Windows、DOS
FAT32 1996年 2TB(实际受限制) Windows 95 OSR2及以后
exFAT 2006年 128PB(理论) 大容量U盘、SDXC卡

3. FAT文件系统的核心结构

3.1 整体布局

FAT文件系统将存储介质划分为几个关键区域:
#mermaid-svg-9MMu7hsQ2xmM2t82{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9MMu7hsQ2xmM2t82 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9MMu7hsQ2xmM2t82 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9MMu7hsQ2xmM2t82 .error-icon{fill:#552222;}#mermaid-svg-9MMu7hsQ2xmM2t82 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9MMu7hsQ2xmM2t82 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9MMu7hsQ2xmM2t82 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9MMu7hsQ2xmM2t82 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9MMu7hsQ2xmM2t82 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9MMu7hsQ2xmM2t82 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9MMu7hsQ2xmM2t82 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9MMu7hsQ2xmM2t82 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9MMu7hsQ2xmM2t82 .marker.cross{stroke:#333333;}#mermaid-svg-9MMu7hsQ2xmM2t82 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9MMu7hsQ2xmM2t82 p{margin:0;}#mermaid-svg-9MMu7hsQ2xmM2t82 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9MMu7hsQ2xmM2t82 .cluster-label text{fill:#333;}#mermaid-svg-9MMu7hsQ2xmM2t82 .cluster-label span{color:#333;}#mermaid-svg-9MMu7hsQ2xmM2t82 .cluster-label span p{background-color:transparent;}#mermaid-svg-9MMu7hsQ2xmM2t82 .label text,#mermaid-svg-9MMu7hsQ2xmM2t82 span{fill:#333;color:#333;}#mermaid-svg-9MMu7hsQ2xmM2t82 .node rect,#mermaid-svg-9MMu7hsQ2xmM2t82 .node circle,#mermaid-svg-9MMu7hsQ2xmM2t82 .node ellipse,#mermaid-svg-9MMu7hsQ2xmM2t82 .node polygon,#mermaid-svg-9MMu7hsQ2xmM2t82 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9MMu7hsQ2xmM2t82 .rough-node .label text,#mermaid-svg-9MMu7hsQ2xmM2t82 .node .label text,#mermaid-svg-9MMu7hsQ2xmM2t82 .image-shape .label,#mermaid-svg-9MMu7hsQ2xmM2t82 .icon-shape .label{text-anchor:middle;}#mermaid-svg-9MMu7hsQ2xmM2t82 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9MMu7hsQ2xmM2t82 .rough-node .label,#mermaid-svg-9MMu7hsQ2xmM2t82 .node .label,#mermaid-svg-9MMu7hsQ2xmM2t82 .image-shape .label,#mermaid-svg-9MMu7hsQ2xmM2t82 .icon-shape .label{text-align:center;}#mermaid-svg-9MMu7hsQ2xmM2t82 .node.clickable{cursor:pointer;}#mermaid-svg-9MMu7hsQ2xmM2t82 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9MMu7hsQ2xmM2t82 .arrowheadPath{fill:#333333;}#mermaid-svg-9MMu7hsQ2xmM2t82 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9MMu7hsQ2xmM2t82 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9MMu7hsQ2xmM2t82 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9MMu7hsQ2xmM2t82 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9MMu7hsQ2xmM2t82 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9MMu7hsQ2xmM2t82 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9MMu7hsQ2xmM2t82 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9MMu7hsQ2xmM2t82 .cluster text{fill:#333;}#mermaid-svg-9MMu7hsQ2xmM2t82 .cluster span{color:#333;}#mermaid-svg-9MMu7hsQ2xmM2t82 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9MMu7hsQ2xmM2t82 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9MMu7hsQ2xmM2t82 rect.text{fill:none;stroke-width:0;}#mermaid-svg-9MMu7hsQ2xmM2t82 .icon-shape,#mermaid-svg-9MMu7hsQ2xmM2t82 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9MMu7hsQ2xmM2t82 .icon-shape p,#mermaid-svg-9MMu7hsQ2xmM2t82 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9MMu7hsQ2xmM2t82 .icon-shape .label rect,#mermaid-svg-9MMu7hsQ2xmM2t82 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9MMu7hsQ2xmM2t82 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9MMu7hsQ2xmM2t82 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9MMu7hsQ2xmM2t82 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 引导扇区

Boot Sector
文件分配表

FAT区域
根目录区

Root Directory
数据区

Data Area

3.2 引导扇区(Boot Sector)

引导扇区位于分区的第一个扇区(第0扇区),包含文件系统的基本参数:

  • 跳转指令:用于引导操作系统
  • OEM标识:厂商信息
  • BPB(BIOS Parameter Block):每扇区字节数、每簇扇区数、保留扇区数、FAT表数量等关键参数
  • 引导代码:用于加载操作系统

3.3 文件分配表(FAT)

FAT是文件系统的核心,它是一个数组结构,每个表项对应数据区中的一个簇(Cluster)。表项的值表示:

  • 0x0000:空闲簇
  • 0x0002-0xFFEF:文件的下一个簇号
  • 0xFFF0-0xFFF6:保留簇
  • 0xFFF7:坏簇
  • 0xFFF8-0xFFFF:文件结束标记(EOF)

3.4 根目录区

根目录区存储文件和子目录的目录项。每个目录项占用32字节,包含:

  • 文件名(8字节)和扩展名(3字节)
  • 文件属性(1字节)
  • 保留字段(10字节)
  • 创建时间(2字节)和创建日期(2字节)
  • 最后访问日期(2字节)
  • 起始簇号高16位(2字节)
  • 最后修改时间(2字节)和日期(2字节)
  • 起始簇号低16位(2字节)
  • 文件大小(4字节)

3.5 数据区

数据区是实际存储文件内容的地方,以簇为单位进行分配。簇的大小由分区容量决定,容量越大,簇通常也越大。

4. FAT表项详解

4.1 FAT12

FAT12使用12位(1.5字节)表示一个簇号,最多管理4096个簇。由于表项不是整数字节,读取时需要特殊处理:

c 复制代码
// FAT12表项读取示例
unsigned int read_fat12_entry(unsigned char *fat, unsigned int cluster) {
    unsigned int offset = cluster + (cluster / 2);
    unsigned int value;
    
    if (cluster & 1) {
        // 奇数簇号,取高12位
        value = (fat[offset] >> 4) | (fat[offset + 1] << 4);
    } else {
        // 偶数簇号,取低12位
        value = fat[offset] | ((fat[offset + 1] & 0x0F) << 8);
    }
    return value;
}

4.2 FAT16

FAT16使用16位(2字节)表示一个簇号,最多管理65536个簇。表项读取相对简单:

c 复制代码
// FAT16表项读取示例
unsigned short read_fat16_entry(unsigned char *fat, unsigned int cluster) {
    unsigned int offset = cluster * 2;
    return (unsigned short)(fat[offset] | (fat[offset + 1] << 8));
}

4.3 FAT32

FAT32使用32位(4字节)表示一个簇号,但实际只使用低28位,最多管理约2.68亿个簇。FAT32还引入了根目录可扩展的特性,根目录不再固定大小,而是像普通文件一样存储在数据区。

5. 文件存储与链式结构

5.1 链式存储原理

FAT文件系统采用链式存储方式管理文件。与连续存储不同,一个文件的数据可以被分散存放在数据区中多个不连续的簇里,系统并不要求这些簇在物理上相邻。

那么FAT如何把这些分散的簇组织成一个完整的文件?关键就在于文件分配表(FAT):

  • 目录项只记录起始簇号:当我们在目录区找到某个文件的目录项时,它保存的是该文件占用的第一个簇的编号,后续位置并不直接写在目录项中。
  • FAT表项负责指向下一簇:数据区中的每个簇在FAT表中都有一个对应的表项。如果文件还需要继续往后延伸,该簇对应的FAT表项就保存"下一个簇号";如果该簇已经是文件最后一簇,FAT表项则填入EOF结束标记。
  • 形成一条"簇链":从目录项给出的起始簇出发,沿着FAT表项不断跳转,就能按顺序找到文件占用的所有簇。

下面的流程图展示了一个占用4个簇的文件链式结构:
#mermaid-svg-tJmGAXpJftJVXr3L{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tJmGAXpJftJVXr3L .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tJmGAXpJftJVXr3L .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tJmGAXpJftJVXr3L .error-icon{fill:#552222;}#mermaid-svg-tJmGAXpJftJVXr3L .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tJmGAXpJftJVXr3L .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tJmGAXpJftJVXr3L .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tJmGAXpJftJVXr3L .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tJmGAXpJftJVXr3L .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tJmGAXpJftJVXr3L .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tJmGAXpJftJVXr3L .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tJmGAXpJftJVXr3L .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tJmGAXpJftJVXr3L .marker.cross{stroke:#333333;}#mermaid-svg-tJmGAXpJftJVXr3L svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tJmGAXpJftJVXr3L p{margin:0;}#mermaid-svg-tJmGAXpJftJVXr3L .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tJmGAXpJftJVXr3L .cluster-label text{fill:#333;}#mermaid-svg-tJmGAXpJftJVXr3L .cluster-label span{color:#333;}#mermaid-svg-tJmGAXpJftJVXr3L .cluster-label span p{background-color:transparent;}#mermaid-svg-tJmGAXpJftJVXr3L .label text,#mermaid-svg-tJmGAXpJftJVXr3L span{fill:#333;color:#333;}#mermaid-svg-tJmGAXpJftJVXr3L .node rect,#mermaid-svg-tJmGAXpJftJVXr3L .node circle,#mermaid-svg-tJmGAXpJftJVXr3L .node ellipse,#mermaid-svg-tJmGAXpJftJVXr3L .node polygon,#mermaid-svg-tJmGAXpJftJVXr3L .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tJmGAXpJftJVXr3L .rough-node .label text,#mermaid-svg-tJmGAXpJftJVXr3L .node .label text,#mermaid-svg-tJmGAXpJftJVXr3L .image-shape .label,#mermaid-svg-tJmGAXpJftJVXr3L .icon-shape .label{text-anchor:middle;}#mermaid-svg-tJmGAXpJftJVXr3L .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tJmGAXpJftJVXr3L .rough-node .label,#mermaid-svg-tJmGAXpJftJVXr3L .node .label,#mermaid-svg-tJmGAXpJftJVXr3L .image-shape .label,#mermaid-svg-tJmGAXpJftJVXr3L .icon-shape .label{text-align:center;}#mermaid-svg-tJmGAXpJftJVXr3L .node.clickable{cursor:pointer;}#mermaid-svg-tJmGAXpJftJVXr3L .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tJmGAXpJftJVXr3L .arrowheadPath{fill:#333333;}#mermaid-svg-tJmGAXpJftJVXr3L .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tJmGAXpJftJVXr3L .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tJmGAXpJftJVXr3L .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tJmGAXpJftJVXr3L .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tJmGAXpJftJVXr3L .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tJmGAXpJftJVXr3L .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tJmGAXpJftJVXr3L .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tJmGAXpJftJVXr3L .cluster text{fill:#333;}#mermaid-svg-tJmGAXpJftJVXr3L .cluster span{color:#333;}#mermaid-svg-tJmGAXpJftJVXr3L div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tJmGAXpJftJVXr3L .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tJmGAXpJftJVXr3L rect.text{fill:none;stroke-width:0;}#mermaid-svg-tJmGAXpJftJVXr3L .icon-shape,#mermaid-svg-tJmGAXpJftJVXr3L .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tJmGAXpJftJVXr3L .icon-shape p,#mermaid-svg-tJmGAXpJftJVXr3L .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tJmGAXpJftJVXr3L .icon-shape .label rect,#mermaid-svg-tJmGAXpJftJVXr3L .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tJmGAXpJftJVXr3L .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tJmGAXpJftJVXr3L .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tJmGAXpJftJVXr3L :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} FAT表项指向
FAT表项指向
FAT表项指向
簇5

起始簇
簇9
簇12
簇20

EOF标记

在这个例子中,目录项里记录的起始簇号是5;系统读取簇5后查询FAT表,发现簇5对应"下一簇为9";继续读簇9,又得知下一簇为12;直到簇20对应的FAT表项出现EOF标记,说明文件到此结束。

5.2 文件读取流程

完整的文件读取过程可以概括为"先找目录项,再沿簇链逐个读取":

  1. 定位目录项:根据文件路径,在目录区中找到该文件对应的32字节目录项。
  2. 获取起始簇号:从目录项中读取文件起始簇号(低16位,FAT32还可能需要结合高16位)。
  3. 读取簇数据:根据簇号计算出该簇在数据区中的地址,读取这一簇内容。
  4. 查询FAT表:在FAT表中找到当前簇对应的表项,获得下一簇号或EOF标记。
  5. 继续或结束:如果得到的是下一簇号,则回到第3步继续读取;如果遇到EOF标记,则文件读取完成。

下面举例说明。假设磁盘上有一个文件 REPORT.TXT,其目录项记录的起始簇号为5,FAT表中相关表项如下:

当前簇 FAT表项值 含义
5 9 下一簇为9
9 12 下一簇为12
12 20 下一簇为20
20 0xFFFF EOF,文件结束

系统读取该文件的完整过程为:

  1. 在目录区找到 REPORT.TXT 的目录项,读取出起始簇号:5。
  2. 计算簇5在数据区的地址,读取出文件的第一段数据(簇5的内容)。
  3. 查询FAT表中簇5对应的表项,得到值 9,说明下一簇是9。
  4. 读取簇9的内容,再查FAT表,得到值 12,说明下一簇是12。
  5. 读取簇12的内容,再查FAT表,得到值 20,说明下一簇是20。
  6. 读取簇20的内容,再查FAT表,发现表项为 0xFFFF(EOF),表示文件全部读取完毕。

最终,系统按照 5 → 9 → 12 → 20 的顺序,把这4个簇的数据拼接起来,就还原了完整的 REPORT.TXT 文件内容。

5.3 碎片问题

链式存储的一个显著缺点是文件碎片化。随着文件的创建和删除,空闲簇变得分散,导致文件被分散存储在多个不连续的簇中。虽然FAT通过链式结构能够正确读取碎片化文件,但频繁的磁盘读写会降低性能。

6. 目录结构

6.1 目录项格式

目录项中与"定位文件"关系最密切的,每个目录项里对应一个文件,目录项里有对应文件的起始簇号。FAT的目录项是32字节的固定结构,具体字段如下:

偏移 长度 字段 说明
0x00 8 文件名 不足8字节用空格填充
0x08 3 扩展名 不足3字节用空格填充
0x0B 1 属性 位标志
0x0C 10 保留 通常为0
0x16 2 创建时间 时:分:秒
0x18 2 创建日期 年:月:日
0x1A 2 最后访问日期 年:月:日
0x1C 2 起始簇号高16位 FAT32使用
0x1E 2 最后修改时间 时:分:秒
0x20 2 最后修改日期 年:月:日
0x22 2 起始簇号低16位 所有版本使用
0x24 4 文件大小 字节数

目录区是什么:目录区是存放目录项的地方,一组连续存放的 32 字节目录项。系统找文件时,本质上就是在对应目录的目录区里逐个比对这些目录项。

目录区存在哪里:

  • FAT12/FAT16:根目录区是一块独立、固定的区域,位于最后一个 FAT 表之后、数据区之前,大小在格式化时确定;子目录不放在这里,而是作为数据存储到数据区中。
  • FAT32:根目录不再占用独立固定区域,根目录本身也像一个"目录文件"存储在数据区中,引导扇区的 BPB 参数会给出根目录的第一个簇号。

要理解根目录到底在哪里,关键一步是先回到分区最前面的引导扇区 读取 BPB 参数。下面用一个从引导扇区出发、最终打开 /DIR1/REPORT.TXT 的完整例子说明。

第一步:从引导扇区读取 BPB,确定根目录位置

系统先读第 0 扇区(引导扇区),取出 BPB 中的关键参数:

  • 每扇区字节数(偏移 0x0B)
  • 保留扇区数(偏移 0x0E)
  • FAT 表数量(偏移 0x10)
  • 每个 FAT 表占用扇区数(FAT12/FAT16 在偏移 0x16,FAT32 在偏移 0x24)
  • 根目录目录项数量(偏移 0x11,FAT32 中为 0)
  • FAT32 还会给出根目录首簇号(偏移 0x2C)

这里需要区分:FAT12/FAT16 的根目录没有"起始簇号"的概念,它是一块固定扇区区域;FAT32 的根目录才是通过"根目录首簇号"定位的目录内容。

  • FAT12/FAT16:根目录区是固定区域,起始扇区号可以计算:

    复制代码
    根目录区起始扇区 = 保留扇区数 + FAT数量 × 每个FAT表扇区数

    例如保留扇区数为 1、FAT 数量为 2、每个 FAT 表占用 16 个扇区,则根目录区从 1 + 2 × 16 = 33 号扇区开始。

  • FAT32 :根目录已经像普通目录一样放在数据区中,不需要用上面的固定区域公式,而是直接读取 BPB 扩展字段中的根目录首簇号 (偏移 0x2C,通常为 2),再按每簇扇区数换算成数据区中的实际地址。

第二步:从根目录开始,逐层查找 /DIR1/REPORT.TXT

拿到根目录位置后,再从根目录 / 开始查找:

  1. 读取根目录区 :FAT12/FAT16 直接到上面算出的根目录扇区读取目录项;FAT32 则到数据区读取根目录首簇对应的目录内容。逐个比对根目录里的目录项,找到目录项 DIR1。该目录项属性为"目录",记录的是 DIR1 子目录内容所在的起始簇号。
  2. 进入 DIR1 的目录区 :根据 DIR1 目录项中的起始簇号,到数据区读取 DIR1 的目录内容。这里读出来的仍然是一组目录项,并不是文件正文。
  3. 在 DIR1 目录区中查找 REPORT.TXT :逐个比对 32 字节目录项,找到 REPORT.TXT 对应的目录项。
  4. 读取起始簇号字段 :
    • FAT12/FAT16:直接读取偏移 0x22 处的 2 字节作为起始簇号,此时偏移 0x1C 的高 16 位保留不用。
    • FAT32:将偏移 0x1C 的 2 字节作为高 16 位、偏移 0x22 的 2 字节作为低 16 位,组合成 32 位起始簇号:起始簇号 = (高16位 << 16) | 低16位。
  5. 进入数据区读取文件正文 :根据起始簇号和 BPB 参数换算出第一个簇在数据区中的地址,读取文件的第一段内容;后续再沿 FAT 表链继续读取下一簇,直到遇到 EOF。
    例如在 FAT32 分区中,REPORT.TXT 目录项的两个起始簇号字段为:
  • 偏移 0x1C:0x0001
  • 偏移 0x22:0x0005

则起始簇号为 0x00010005,即文件从第 0x00010005 号簇开始存储。对于 FAT12/FAT16,由于高位字段固定为 0,起始簇号就只剩低 16 位参与计算,直接读 0x22 字段即可。

6.2 长文件名支持

传统的FAT使用8.3命名规则(8字符文件名+3字符扩展名),无法满足现代用户的需求。为此,FAT引入了VFAT(Virtual FAT)长文件名扩展:

  • 使用多个连续的目录项存储长文件名
  • 每个目录项可存储13个UTF-16字符
  • 通过属性字段中的特殊标志(0x0F)识别
  • 保持向后兼容性,旧系统仍可读取短文件名

6.3 目录层级

FAT12和FAT16的根目录位于固定位置且大小固定,而子目录则存储在数据区中,可以动态扩展。FAT32的根目录也支持动态扩展,与普通目录类似。

7. FAT32与exFAT的深入对比

7.1 FAT32的特点

  • 最大分区容量:理论2TB,但Windows格式化工具限制为32GB
  • 最大文件大小:4GB(受32位文件大小字段限制)
  • 簇大小:随分区容量增大而增大,最小4KB
  • 兼容性:几乎所有操作系统和设备都支持

7.2 exFAT的改进

exFAT(Extended File Allocation Table)是微软为闪存设备设计的现代文件系统:

  • 最大文件大小:16EB(理论)
  • 最大分区容量:128PB(理论)
  • 支持权限控制:可选的访问控制列表
  • 优化闪存性能:减少写入次数,延长寿命
  • 支持大簇:最大32MB

7.3 选择建议

使用场景 推荐文件系统 原因
软盘、老旧设备 FAT12/FAT16 兼容性最好
U盘(≤32GB) FAT32 兼容性广,系统支持好
U盘(>32GB) exFAT 突破大小限制
SD卡(SDXC) exFAT 标准要求
跨平台移动存储 exFAT Windows/macOS/Linux均支持

8. FAT文件系统的优缺点

8.1 优点

  • 简单高效:结构清晰,实现简单,资源占用少
  • 兼容性极强:几乎所有操作系统、嵌入式设备、相机、游戏机都支持
  • 轻量级:元数据开销小,适合小容量存储
  • 易于恢复:结构简单,数据恢复相对容易

8.2 缺点

  • 无日志功能:突然断电可能导致文件系统损坏
  • 碎片化严重:链式存储导致文件碎片
  • 无权限控制:不支持文件级权限和加密
  • 单文件大小限制:FAT32最大4GB
  • 可靠性较低:FAT表损坏可能导致整个文件系统不可用

9. 实际应用与工具

9.1 常见应用场景

  • 可移动存储:U盘、SD卡、移动硬盘
  • 嵌入式系统:数码相机、MP3播放器、车载系统
  • 双系统共享:Windows与Linux之间的数据交换
  • 引导分区:部分系统使用FAT作为EFI系统分区

9.2 常用工具

  • Windows:磁盘管理、format命令
  • Linux:mkfs.vfat、fsck.vfat
  • macOS:磁盘工具
  • 跨平台:Rufus、balenaEtcher

10. 总结

FAT文件系统虽然诞生于40多年前,但凭借其简单性和无与伦比的兼容性,至今仍在存储领域占据重要地位。从FAT12到exFAT,它不断演进以适应存储技术的发展。

理解FAT文件系统的内部结构,不仅有助于我们更好地使用存储设备,还能在数据恢复、嵌入式开发、底层系统编程等场景中发挥重要作用。尽管现代文件系统(如NTFS、ext4、APFS)在功能和性能上更为强大,但FAT系列依然是跨设备数据交换的"通用语言"。

希望本文能帮助你全面理解FAT文件系统。如果你有任何疑问或想深入了解某个方面,欢迎在评论区留言讨论!

相关推荐
看着博客敲代码1 小时前
enum4linux 报 Server doesn‘t allow session 与 SMB 主机信息枚举
linux·安全·网络安全
重生之小比特1 小时前
【Linux系统】09 环境变量
linux·运维·服务器
Teecertlabs1 小时前
【机密计算顶会解读】15:LDR——面向嵌入式TEE系统的高效安全Linux驱动
linux·操作系统·机密计算·可信执行环境
程序员-Benothing1 小时前
Linux网络性能分析:ss、netstat、iftop、sar实战
linux·运维·服务器
其实防守也摸鱼1 小时前
Codex 下载与本地部署实战指南
linux·服务器·前端·数据库·算法·ai
岳麓丹枫0011 小时前
故障案例分析:系统负载居高不下,多次出现 OOM
linux·postgresql
2401_862880821 小时前
Linux中断、中断子系统、内核同步互斥机制
linux·驱动开发·嵌入式硬件
姓刘的哦2 小时前
GT911触摸芯片
linux·stm32
Android系统攻城狮2 小时前
Linux Gstreamer深度解析之gst_audio_ring_buffer_read调用流程与实战(七十五)
linux·运维·服务器·gstreamer音视频·gstreamer音视频进阶