Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
FAT
FAT32
elm_fatfs_FAT文件系统
FATFS文件系统详解
发布于 2023-06-04 23:28:22 浏览:4200
订阅该版
[tocm] # FATFS文件系统详解 ## 1. 简介 在早期计算机刚发展的时候,那时候硬盘大小、flash设备容量都比较小,随着技术的不断迭代更新,硬盘容量越来越大。在早期,面对小容量的硬盘/flash,往往采用对应地址存放对应数据的方案,由于数据量不大,操作起来尚还可以。但是发展到今天,随着硬盘/flash容量不断增大,存储的数据也越来越多,早期单一的对应地址存放对应数据的方案已经无法满足我们的需求,操作硬盘/flash会变得异常的困难复杂。 因此针对上述问题,一群大佬们便开始设计文件系统这样一个东西,用来管理硬盘/flash上的数据信息,像我们电脑上打开文件夹,访问里面的文件,这其实就是基于文件系统访问电脑硬盘上数据的一个操作。 发展至今,文件系统已有众多版本,本文主要分享 **关于FAT文件系统的详细设计**, FAT文件系统适用于嵌入式设备,如SD卡、SD nand、spi nor flash等众多存储设备,同时基于此文件系统的文件亦能被电脑正常读取。 ## 2. 基础概念 在研究文件系统之前,我们需要首先弄清楚关于内存这块的几个基本概念: 1. 区分 ==扇区、块、簇== 的概念 + **扇区(sector)**:flash可操作的最小单元,通常指我们擦除的最小单元大小,以sd nand举例,通常最小为512Byte + **块(block) 以及 簇(cluster)**:其实这是两个相同的概念,只是由于历史原因,在不同系统上的不同称呼,在windows中称簇,而在linux中称块。一个簇/块由多个扇区组成,由于一个扇区的空间较小,因此文件系统通过会将多个扇区组合在一起形成一个簇,并以簇为单位进行读写操作! 一个簇通常可以由 2、4、8、... 、2的n次方个扇区组成。 2. FAT文件系统总共由FAT12、FAT16以及FAT32三个版本,这是由于随着存储技术不断发展,FAT文件系统迭代导致,数字越大,版本越新,新版本对老版本完全兼容! ## 3. FAT文件系统组成介绍 > Fat文件系统官方文档:[http://elm-chan.org/docs/fat_e.html](http://elm-chan.org/docs/fat_e.html) FAT文件系统在flash上的布局如下图所示,总共由四个区域组成: + 保留区 + FAT区 + 根目录区 (FAT32类型不包含此区域) + 数据区 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/7e626218350c82b05ff05ee228468c2f.png) ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/9dd19fdc086676618f101bad87b9c95a.png) 接下来,我们对一张格式化为FAT格式的SD卡进行分析,理解FAT文件系统的实现细节: ## 4. FAT文件系统分析 ### 4.1 采用FAT格式格式化SD nand/sd卡 1. 使用win10格式化一张118.5M的SD nand / sd卡,我这里用的是手上的一颗 创世CS 家的sd nand加一块转接板,和SD卡完全没有区别,且SD nand在稳定性上比SD卡具有优势。 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/9272fd8d7d075b8e86485815a163e244.png.webp) ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/135c01f20fa56731e10e24592db460a6.png) ==**此处由于SD nand(SD卡)大小原因,默认采用FAT16进行了格式化!因此在下文中我们先以FAT16进行分析,之后再重新格式化为FAT32进行分析,就很容易懂了!**== ### 4.2 引导扇区分析 1. 使用 `winhex` 工具打开对应磁盘,注意需使用管理员权限运行 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/78ab7adcf6756789baa459413731079c.png) 2. 打开后我们可以以二进制的格式查看SD卡上所有数据,首先看到第一个扇区,也就是对应的引导扇区 boot sector,注意引导扇区位于保留区! ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/ebf93b02fca0d26726bbfcd2f2a152a3.png) 3. 接下来我们根据[官方文档](http://elm-chan.org/docs/fat_e.html) 对引导扇区进行分析 > 注意,FAT文件系统数据均采用小端格式! **a) 首先是FAT12/16/32公共部分,(偏移值 0 - 35):** + `EB 3C 90`:BS_JmpBoot,跳转指令 + `4D 53 44 4F 53 35 2E 30`:BS_OEMName,MSDOS 5.0,一个名字,指示创建此卷的操作系统,无其他作用 + `00 02`:BPB_BytsPerSec,扇区大小 512 字节 + `04`:**BPB_SecPerClus,每次操作的最小扇区数,簇 Cluster,4** (与格式化时选择的大小匹配 2048 = 512 * 4) + `06 00`:**BPB_RsvdSecCnt,保留区的扇区数,6** (通过此可计算,FAT区起始地址为 6 * 512 = 0xC00) + `02`:**BPB_NumFATs,FATs的个数,2**(一般此值为2,多一个用来做冗余备份,解决系统异常导致第一个损坏时,增大恢复的可能性,表示FAT区有两个FATs备份) + `00 02`:BPB_RootEntCnt,512,在FAT12/16系统中,此字段表示根目录中32字节目录条目数量,设置此值时需注意对齐,为了最大的兼容性,FAT16系统上此值应设置为512,FAT32系统上此值应设置为0 + `00 00`:**BPB_TotSec16,16位大小区域描述FAT卷扇区总数,0。当FAT12/16系统扇区数 ≥0x10000(65536)时,此字段应设置为0,真实值存放在 BPB_TotSec32 字段;对于FAT32系统,此值必须为0。(此处由于我们的总扇区数=118.5*1024*1024/512 = 242688 > 65536,所以此字段为0)** + `F8`:BPB_Media 媒体类型 + `ED 00`:**BPB_FATSz16,237,一个FAT占用的扇区数**,此字段仅在FAT12/16系统使用;FAT32系统,此字段必须为0,使用BPB_FATSz32字段替代。FAT区总大小等于 BPB_FATSz?? * BPB_NumFATs 扇区(237*2*512=242688=0x3B400,由此可推算根目录区起始地址:0x3B400+0xC00=0x3C000)。 + `3F 00`:BPB_SecPerTrk,每个磁道的扇区数,此字段仅与具有几何形状且仅用于 IBM PC 的磁盘 BIOS 的介质相关,不用管。 + `FF 00`:BPB_NumHeads,头数量,此字段仅与具有几何形状且仅用于 IBM PC 的磁盘 BIOS 的介质相关,不用管。 + `00 00 00 00`:**BPB_HiddSec,0,FAT 卷之前的隐藏物理扇区数**(当磁盘被分区之后,当前分区并不一定是从扇区头开始的) + `00 B4 03 00`:**BPB_TotSec32,242688,32位大小区域描述FAT卷扇区总数(整个卷空间大小)。** FAT12/16系统,扇区总数小于0x10000时,此字段必须为0,真实值存放在BPB_FATSz16;FAT32系统,此字段一直有效。(118.5M = 512 * 242688) **b) 接下来是FAT12/16特有字段(偏移值36)** + `80`:BS_DrvNum,IBM PC 的磁盘 BIOS 使用的驱动器号,00h代表软盘,80h代表固定磁盘 + `00`:BS_Reserved,保留字段,0 + `29`:BS_BootSig,扩展引导签名,表示以下存在三个字段 + `83 3E 07 E4`:BS_VolID,与 BS_VolLab 一起构成卷序列号,一般在格式化的时候结合时间生成 + `4E 4F 20 4E 41 4D 45 20 20 20 20`:(解析为:"NO NAME "),BS_VolLab,11byte卷标,当卷标不存在时,此值应设置为"NO NAME" + `46 41 54 31 36 20 20 20`:(解析为:"FAT16 "),BS_FilSysType文件系统类型,支持字段有:"FAT12 ", "FAT16 " or "FAT ",**注意很多人认为是通过此字段区分FAT12/16/32系统类型,实际是错误的**,文件系统类型实际上是根据磁盘大小确定的,官方文档 “Determination of FAT sub-type” 章节或本博文后文有描述,不过为了最大的兼容性考虑,此字段应设置为对应文件系统的名字。 + `33 C9 ~ CB D8`:BS_BootCode,引导启动程序,与平台有关,不使用时填充为0 + `55 AA`:BS_BootSign,0xAA55,引导签名,指示这是一个有效的引导扇区 当扇区大小大于512字节时,剩余的字段应全部使用0x0填充。 **c) 如果是FAT32,则采用FAT32特有字段解析(偏移值和FAT12/16特有字段一致为36)** 虽然此处我们的是FAT16格式,不过此处也将FAT的字段进行描述,方便理解。 + `BPB_FATSz32`:一个FAT占用的扇区数,此字段仅在FAT32系统有效。FAT区总大小等于 BPB_FATSz?? * BPB_NumFATs 扇区。 + `BPB_ExtFlags`:扩展标识字段,bit7=0,表示所有FAT都是镜像的和活跃的;bit7=1,表示只有bit3-0表示的FAT是有效的。 + `BPB_FSVer`:FAT32版本,高字节是主版本号,低字节是次版本号。 + `BPB_RootClus`:根目录的第一个簇号,此值通常为2,因为前两个簇一般用于保留。 + `BPB_FSInfo`:FSInfo结构扇区与FAT32卷顶部的偏移扇区值。此值通常为1,因为其通常位于引导扇区旁边。 + `BPB_BkBootSec`:备份引导扇区与FAT32卷顶部的偏移扇区值。此值通常为6,考虑最大的兼容性,此值不建议为其他值。 + `BPB_Reserved`:保留 + `BS_DrvNum`:含义与FAT12/16字段一样 + `BS_Reserved`:含义与FAT12/16字段一样 + `BS_BootSig`:含义与FAT12/16字段一样 + `BS_VolID`:含义与FAT12/16字段一样 + `BS_VolLab`:含义与FAT12/16字段一样 + `BS_FilSysType`:始终为"FAT32 ",对FAT类型的确定没有任何影响。 + `BS_BootCode32`:引导启动程序,与平台有关,不使用时填充为0 + `BS_BootSign`:0xAA55,引导签名,指示这是一个有效的引导扇区 当扇区大小大于512字节时,剩余的字段应全部使用0x0填充。 以上就是引导扇区内容的详细分析了,通过引导扇区的内容,我们即可知道FAT文件系统依赖的硬件存储空间大小、簇大小、扇区大小以及以及FAT系统版本等重要信息。 同时通过引导扇区的内容,我们便可计算出对应的FAT的四个区域的大小及起始偏移位置等重要信息,接下来计算FAT四个分区的起始位置及大小。 ### 4.3 分区偏移及大小计算 FAT卷总共分为以下四个区域: + 保留区 + 第一个扇区为引导扇区,存放BPB(BIOS Parameter Block)数据,存放的是FAT卷的配置参数。 + 上述参数中以 BPB_ 命名的字段都是 BPB 的一部分,而以 BS_ 标题命名的字段都不是 BPB 的一部分,而只是引导扇区的一部分 + FAT区(分区表装载区) + 根目录区 + 数据区 各分区偏移地址及大小如下: | 分区名 | 起始扇区 | 起始地址 | 分区大小 | 备注 | | -- | -- | -- | -- | -- | | 保留区 | 0x00 | 0x00 | 扇区数: `BPB_RsvdSecCnt` = 6
字节数: `BPB_BytsPerSec` ✖ `BPB_RsvdSecCnt` = 6 ✖ 512 = 3072 = ==0xC00== | | | FAT区 | FatStartSector = `BPB_RsvdSecCnt` = 6 | ==0xC00== | 扇区数:FatSectors = `BPB_FATSz??` ✖ `BPB_NumFATs` = 237 ✖ 2 = 474
字节数:474 ✖ 512 = 242688 = ==0x3B400== | | | 根目录区 | RootDirStartSector = FatStartSector + FatSectors = 6 + 474 = 480 | 0x3B400 + 0xC00 = ==0x3C000==| 扇区数:RootDirSectors = (32 ✖ `BPB_RootEntCnt`+ `BPB_BytsPerSec` - 1) / `BPB_BytsPerSec` = (32 * 512 + 512 - 1) / 512 = 32
字节数:32 ✖ `BPB_RootEntCnt` = 32 ✖ 512 = 16384 = ==0x4000== | 根目录区每个目录项占32Byte | | 数据区 | DataStartSector = RootDirStartSector + RootDirSectors = 480 + 32 = 512 | 0x3C000 + 0x4000 = ==0x40000== | 扇区数:DataSectors = `BPB_TotSec` - `DataStartSector` = 242688 - 500 = ==242176== | | 此外,关于FAT区,通常存在一个以上的FAT,如此处所格式化的sd卡便存在两个FAT,对应的偏移地址和大小如下: | FAT | 起始扇区 | 起始地址 | 分区大小 | 备注 | | -- | -- | -- | -- | -- | | FAT1 | 6 | 0xC00 | 扇区数:237
字节数:237 ✖ 512 = 121344 | | | FAT2 | 6 + 237 = 243 | 0xC00 + 237 ✖ 512 = 0x1E600 | 扇区数:237
字节数:237 ✖ 512 = 121344 | | ### 4.4 FAT子类型确认 关于FAT的类型是FAT12/16/32确认:FAT类型由数据区内簇的数量决定,除此之外无其他办法! + 当一个卷,簇的数量 ≤4085 时,为FAT12 + 当一个卷,簇的数量 ≥4086 且 ≤65525 时,为FAT16 + 当一个卷,簇的数量 ≥65526 时,为FAT32 簇的数量计算公式:`CountofClusters` = `DataSectors` / `BPB_SecPerClus`; 如我们这里:`CountofClusters` = ==242176== / 4 = 60544,==所以为 FAT16!== 当簇的大小从 512 ~ 32768字节的各种条件下,不同类型FAT对应卷的大小范围如下: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/80203b3f20b439b0e8bd3a601fc20306.png) ### 4.4 访问FAT条目 FAT区由一条条FAT条目构成,关于 `FAT[N]` 对应的条目具体位置计算如下: + FAT16: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/d0c6fefb558460244935b197511649d9.png) + FAT32: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/739c6379c4b50154f1146eca08d044c6.png) 格外需要注意的是,不同格式,对应的FAT条目的长度和格式不一样: 此外对于FAT32格式,高4位是保留位,只有低28位有效! 具体如下图所示: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/17a6b3126fa8168613db7c921576ef6b.png) ### 4.5 文件与簇之间的关系 那么文件和簇之间的相互关系又是怎样的呢?我们又是如何准确的找到存放在flash上的文件的呢?接下来让我们看下文件与簇之间的关系映射。 在FAT卷上文件通过目录管理,==目录是一个32字节数组组成的目录条目结构==,此目录结构包含:文件名、文件大小、时间戳以及文件所在的第一个簇号。 **簇号为0和1的簇被保留,由参数`BPB_RootClus`可知,有效簇从第2号簇开始。==FAT\[2\](2号簇)对应数据区的第一个簇==。** 因此第N个簇的位置计算公式如下: `FirstSectorofCluster` = `DataStartSector` + (N - 2) * `BPB_SecPerClus` ==每个条目所在的位置,对应一个簇。当文件长度大于一个簇长度时,条目内的值为下一个条目的索引,直到文件所在的最后一个簇,由此构成簇链!文件所在的最有一个簇所对应的FAT条目内的值由一个特殊的值(EOC)组成,它永远不会匹配任何有效的簇号==,如下: + FAT12: 0xFF8 - 0xFFF (typically 0xFFF) + FAT16: 0xFFF8 - 0xFFFF (typically 0xFFFF) + FAT32: 0x0FFFFFF8 - 0x0FFFFFFF (typically 0x0FFFFFFF) 存在一些特殊的值被用来做损坏簇的标记,如下: + FAT12: 0xFF7 + FAT16:0xFFF7 + FAT32:0xFFFFFFF7 不过此处需要注意,在FAT12/16系统上,上述特殊值绝不会和任何有效簇匹配,但是在FAT32上有可能,因此为了防止混淆,你在创建FAT32系统的时候应该避免这种情况发生!因此FAT32系统上簇的上限为268435445(大于256M个簇) FAT条目初始化的时候,FAT[2] 及以后的数据应被初始化为0,指示未被使用处于空闲状态,如果值不为0,则意味着簇被损坏或被使用状态。在FAT12/16系统上,空闲簇的数量未被记录,而在FAT32系统上,FAT32支持FSInfo结构体,里面记录了空闲簇的数量。 **==关于FAT[0]和FAT[1]:==** 此两个保留的条目,没有与任何簇有联系;不过具有其他意义,如下: + FAT12: FAT[0] = 0xF??; FAT[1] = 0xFFF; + FAT16: FAT[0] = 0xFF??; FAT[1] = 0xFFFF; + FAT32: FAT[0] = 0xFFFFFF??; FAT[1] = 0xFFFFFFFF; FAT[0]中的?? 与 `BPB_Media` 相同; FAT[1] 记录了错误历史记录:卷脏标志(FAT16:bit15、FAT32:bit31),系统在启动的时候清除此位,正常关闭的时候恢复。 如果此位已经清除,表明上次未被正常关闭,可能存在逻辑卷错误;硬件错误标志(FAT16:bit14、FAT32:bit30)当出现无法恢复的读写错误时清除,表明需要进行全面检查。 ==**关于FAT区域,有两个重点注意事项:**== 1. 第一个是FAT的最后一个扇区可能没有被完全使用。在大多数情况下,FAT在扇区的中间结束。FAT驱动程序不应该对未使用的区域做出任何假设。在格式化卷时,应该用零填充它,并且在此之后不应更改它。 2. 另一个是BPB_FATSz16/32可以指示比卷需要的值大的值。换句话说,未使用的扇区可以跟随每个FAT。这可能是数据区域对齐或其他原因导致的。同时,在格式化时这些扇区也会被用零填充。 下表展示了不同FAT类型中FAT值所对应的含义解释: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/c0d6bbf465581bc8be75a20511c008e9.png) ### 4.6 FSInfo扇区结构及备份引导扇区 此部分内容只在FAT32系统上存在,对于FAT12系统FAT区域大小最大6KB,对于FAT16系统FAT区域最大128KB,但是在FAT32系统上FAT区域通常上达数MB,这是因为FAT32系统支持FSInfo数据结构。 在FAT32系统上新增FSInfo数据结构的原因是:在FAT12/16系统上,想要知道flash上剩余的簇数需要扫描整个FAT区才能计算出来,但随着flash容量的不断扩大,扫描花费的时长越来越长,为了避免扫描浪费过多的时间,因此在FAT32系统上增加了FSInfo结构,用于记录flash上剩余的簇数。 FSInfo数据结构如下: | 字段名 | 偏移 | 大小 | 描述 | | -- | -- | -- | -- | | FSI_LeadSig | 0 | 4 | 固定值为0x41615252,头部签名 | | FSI_Reserved1 | 4 | 480 | 保留区域,采用0x00覆盖 | | FSI_StrucSig | 484 | 4 | 固定值为0x61417272,也是一个签名 | | FSI_Free_Count | 488 | 4 | 记录了空闲的簇数,如果这个值为0xFFFF FFFF,则表示不知道具体的空闲簇数 | | FSI_Nxt_Free | 492 | 4 | 提示驱动程序应该从此参数提示的簇开始寻找空闲的簇,通过此参数便可以不用从FAT区头开始寻找下一个空闲簇了,节省了大量时间;如果此参数为0xFFFF FFFF,则驱动程序应该从头部(2号簇)开始寻找空闲簇| | FSI_Reserved2 |496 | 12 | 保留区域,采用0x00覆盖 | | FSI_TrailSig | 508 | 4 | 固定值0xAA550000,尾部签名 | 注意:当扇区大小大于512字节时, 剩余空间采用0x00填充 ### 4.7 FAT目录 FAT目录分为长文件名目录(LFN)以及短文件名目录(SFN),长文件目录是在短文件名目录上的一个扩展,具体采用长文件名还是短文件名由读取FAT文件系统的操作系统决定,如windows;设置长文件名时短文件名也被设置,具有兼容性。 此外,有一个很重要的概念:**在FAT文件系统上目录也是一个文件,只是此文件的属性不一样而已**。 在所有目录中,有一个比较特殊的是**根目录**,且根目录作为顶层目录必须存在。 + 在FAT12/16系统中,根目录不是一个文件,且放在根目录区,根目录的最大条目数由 `BPB_RootEntCnt` 参数指示; + 在FAT32系统中,根目录与子目录没有什么区别,且根目录的起始簇由 `BPB_RootClus` 参数指示。 + 根目录与子目录的另外一个区别是,根目录不包含 `.` `..` 此两个点目录,且它可以包含卷标(具有`ATTR_VOLUME_ID`属性的条目) ### 4.7.1 SFN 短文件名目录 目录条目结构如下: | 字段名 | 偏移 | 大小 | 说明 | | -- | -- | -- | :-- | | DIR_Name | 0 | 11 | 短文件名 | | DIR_Attr | 11 | 1 | 文件属性标志,由以下标志组合构成(高2位保留且为0)
0x01: `ATTR_READ_ONLY` (Read-only)
0x02: `ATTR_HIDDEN` (Hidden)
0x04: `ATTR_SYSTEM` (System)
0x08:` ATTR_VOLUME_ID` (Volume label) 根目录项中有且仅有一个目录具有此属性,且具有此属性的目录 DIR_FstClusHI, DIR_FstClusLO, DIR_FileSize 字段需要为0
0x10: `ATTR_DIRECTORY` (Directory)
0x20: `ATTR_ARCHIVE` (Archive) 备份使用
0x0F: `ATTR_LONG_FILE_NAME` (LFN entry) | | DIR_NTRes | 12 | 1 | 指示 SFN 大小写信息的可选标志
0x08: 文件名内的每个字符都是小写
0x10: 扩展名内每个字符都是小写 | | DIR_CrtTimeTenth | 13 | 1 | 可选的次秒级信息对应于 `DIR_CrtTime`。`DIR_CrtTime` 的时间分辨率为2秒,因此该字段给出的计数为亚秒,其有效值范围为0到199,单位为10毫秒。如果不受支持,则设置为零,以后不要更改。 | | DIR_CrtTime | 14 | 2 | 可选的创建文件的时间,如果不支持,应设置为0且之后不能被改变 | | DIR_CrtDate | 16 | 2 | 可选的创建文件的日期,如果不支持,应设置为0且之后不能被改变 | | DIR_LstAccDate | 18 | 2 | 可选的最后访问日期,如果不支持,应设置为0且之后不能被改变 | | DIR_FstClusHI | 20 | 2 | ==指示文件所在簇号的上半部分==,在FAT12/16系统上始终为0 | | DIR_WrtTime | 22 | 2 | 文件被修改的最后时间 | | DIR_WrtDate | 24 | 2 | 文件被修改的最后日期 | | DIR_FstClusLO | 26 | 2 | ==指示文件所在簇号的下半部分==,注意当文件大小为0时,不会有簇分配,此时此值为0;当此文件为一个目录时,此值始终为有效值 | | DIR_FileSize | 28 | 4 | 文件大小,单位:字节;当此文件为目录时,此参数未被使用,且应该设为0 | 关于目录结构的第一个字段 `DIR_Name` 的第一个元素 `DIR_Name[0]` 在目录表中有着特殊作用,如下: + 当此值为 `0xE5` 时,代表此目录条目未被使用(或已废弃) + 当此值为 `0x00` 时,也代表此目录条目未被使用;此外还提示后续目录条目也未被使用,因为后续的目录条目 `DIR_Name[0]` 都会是 0x00 + 如果文件名的第一个字符为 `0xE5` 这个特殊值,则使用 `0x05` 替代。 这么设计的意义是什么呢?将 `DIR_Name[0]` 用作特殊字符,其目录在于方便文件删除!当我们删除一个文件的时候,文件系统并不会将此文件所对应的数据全部删除,因为那样太费时间了,也没有必要,而是直接将对应文件的目录项中的 `DIR_Name[0]` 修改为 `0xE5` 即可! --- 关于文件名字段 `DIR_Name`,在FAT文件系统中还有如下规定: + `DIR_Name` 字段的11字节的文件名分为两个部分:**8 字节的主文件名 + 3字节的扩展名**; + 文件名中主文件名与扩展名中间的 `.` 被省略,不在此记录 + 如果主文件名长度不够,小于8字节,则使用 `0x20` 空格填充 + 用于设置文件名的字符也有限制,支持的字符有 ==0~9 A~Z ! # $ % & ' ( ) - @ ^ _ ` { } ~== + 主文件名和扩展名中的(a~z)ASCII字符都会被**转化成大写字符保存** 以下为文件名存储示例: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/d881471492911cece38e0c85a91b4806.png) ### 4.7.2 LFN长文件名 长文件名是文件名的另外一种存储方式,由于SFN短文件名具有长度、字符等限制,在一些场景下不能很好的满足需求,因此就需要使用到长文件名,关于长文件名的具体内容如下: 长文件名是一个具有特殊属性的目录条目。长文件名目录属性 `DIR_Attr` 字段的值 `ATTR_LONG_NAME ` = `(ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)` = `0x0F`; ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/7ba1350889457791344961e87f73c91e.png.webp) 关于长文件名的目录属性如下: | 字段名 | 偏移 | 大小 | 描述 | | -- | -- | -- | -- | | LDIR_Ord | 0 | 1 | 序号(1-20),用来表示此条目属于长文件名序列条目中的第几条。且长文件名序列首条条目的值应`& 0x40`以进行标识! | | LDIR_Name1 | 1 | 10 | 长文件名 第1 ~ 第5 字符(注意此处一个字符占两个字节) | | LDIR_Attr | 11 | 1 | 长文件名属性,此值永远为 `ATTR_LONG_NAME 0x0F` | LDIR_Type | 12 | 1 | 类型,必须为0 | | LDIR_Chksum | 13 | 1 | 和校验 | | LDIR_Name2 | 14 | 12 | 长文件名 第6 ~ 第11 字符(注意此处一个字符占两个字节) | | LDIR_FstClusLO | 26 | 2 | 必须为0 | | LDIR_Name3 | 28 | 4 | 长文件名 第12 ~ 第13 字符(注意此处一个字符占两个字节) | 关于长文件名,有以下几点重要概念: + 一个文件一定有短文名SFN,但不一定有长文件名LFN + 长文件名LFN字段中==仅包含文件名信息==,不包含其他内容,其他内容需要通过短文件名SFN查看 + 如果一个文件既有长文件名也有短文件名,则长文件名是其主要名字,而短文件名则为附加名字 + ==长文件名LFN条目在对应的短文件名SFN条目前面== + 一个文件的长文件名最长255字符,对应最多20个长文件名LFN条目 + 长文件名简单理解起始就是存储一个字符串,因此没有类似SFN的限制,允许有空格、支持大小写、允许多个`.`符号等 + LFN条目文件名长度不够,仍然采用`0x20`填充 下图是官方关于一个名为 "MultiMediaCard System Summary.pdf" 的长文件名在flash上的长文件名条目,如下所示,一眼没看明白也没关系,后文有实例说明,对长文件名有概念了就行! ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/cff49b5f405f0744b285c688c6a9d8f5.png) 关于长文件名的checksum字段和计算,算法如下: ```c uint8_t create_sum (const DIR* entry) { int i; uint8_t sum; for (i = sum = 0; i < 11; i++) { /* Calculate sum of DIR_Name[] field */ sum = (sum >> 1) + (sum << 7) + entry->DIR_Name[i]; } return sum; } ``` ### 4.7.3 LFN系统对于SFN的兼容 在使用LFN长文件名的系统中,会自动生成SFN短文件名已确保此文件在短文件名的文件系统中可使用。同时为了防止生成的短文件名冲突,SFN的生成采用 **名称+数字后缀+扩展** 的格式,同时采用以下规则生成SFN: 1. 小写自动转大写 2. 如果存在空格,则删去空格,设置有损转换标识 3. 已`.`开头的文件删除头部的`.`,并设置有损转换标识 4. 存在多个`.`的文件名,仅保留最后一个作为文件名与扩展的分隔,并设置有损转换标识 5. 其他不支持的字符,采用`_`代替,并设置有损转换标识 6. 文件名如果是Unicode编码,则转化为ANSI/OEM编码;不能转换的字符采用`_`代替,并设置有损转换标识 7. 长度超过8字节的部分,截断,并设置有损转换标识 8. 扩展名字段超过3字节的,截断,并设置有损转换标识 有损转转换标识为:`~`,ASCII值为`0x7E`,十进制126 示例如下: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/d83b1fb141dcf7714b8fcd2bcf0a63f2.png) --- 至此,FAT文件系统的理论部分已经描述完了,接下来我们继续使用winhex对数据进行分析。 ## 5. 分区分析 继续回顾我们一开始的这张布局图 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/9dd19fdc086676618f101bad87b9c95a.png) ### 5.1 保留分区分析 保留分区为第一个分区,其中引导扇区位于保留分区的第一个扇区。 根据 4.3 章节计算结果可知,保留分区**起始地址为 0x00**,**大小 0xC00** 保留分区数据如下,保留分区内最重要的内容即为引导扇区,除引导扇区外,其他剩余空间全部保留,采用0x00覆盖。关于引导扇区已在 4.2 章节详细分析,此处不再做介绍。 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/6040e7aaecf6210d8af118cb0de4ec3e.png) ### 5.2 FAT区分析 根据 4.3 章节描述,FAT区的起始地址为 ==0xC00==,大小为 ==0x3B400==,此外存在两个FAT区,FAT1和FAT2,起始地址分别为:==0xC00==、==0x1E600==,对应地址数据如下: FAT1 数据: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/befb6b133b2579378aa080486aa47d97.png.webp) FAT2 数据如下: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/efe1811523ce33c046741d9ae980ec02.png.webp) ==由于此处采用FAT16格式,所以每个FAT条目占据两个字节!== 根据上述数据进行分析: 1. 确认 FAT2 为 FAT1 的备份; 2. 存在5个FAT条目其中 FAT[0] 和 FAT[1] 为保留条目,FAT[0] 的内容与 `BPB_Media` 媒体类型字段一致,FAT[1] 用来记录错误历史记录 (详见 4.5 章节描述) 3. ==根据4.5章节描述,FAT\[2\]\(2号簇\)对应数据区的第一个簇==,又FAT[2]、FAT[3]、FAT[4] 数据均为 0xFF,表明存在三个文件,且每个文件的大小小于等于一个簇的空间;且分别存放在数据区第1到第3个簇上! 此处可能大家会由疑问,刚刚格式化的sd卡为什么会存在文件内,其实这个是系统文件,格式化后自带的,默认是隐藏的,只有使用winhex才能看到,也就是对应的`System Volume Information`文件夹。 ### 5.3 根目录区分析 注意,根目录区只有 FAT12 / FAT16 系统上存在,在FAT32系统上不存在此区域。 根目录区用来记录根目录下的文件内容,根据 4.3 章节计算可知,根目录区起始地址为:==0x3C000==,大小为==0x4000==,数据内容如下: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/cee2374811e54db00ebe8d2df0eb40a1.png.webp) 以下是对数据字段进行分析后的内容,如下图所示: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/75f9b09949239bd4a0fd47888781aa9a.png.webp) 格式化之后,默认会生成一个`System Volume Infomation`的系统文件夹,同时此文件夹是根目录下唯一的一个文件,因此在根目录的数据如上图所示。 1. 此文件夹为目录属性,是隐藏的系统目录 2. 长文件名为`System Volume Information`,短文件名为`SYSTEM~1` 3. ==此目录指向存放的数据在2号簇(对应数据区第一个簇),文件大小字段,由于此文件为目录属性,此字段无意义,因此强制为0== 至此,根目录区分析完了,同时根目录区的 `System Volume Information`文件指向数据区第一个簇(2号簇),接下来我们便进入数据区进行分析。 ### 5.4 数据区分析 根据 4.3 章节计算可知,数据区起始地址为:0x40000,大小为`242176 * 512 = 0x764 0000`,数据内容如下: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/048a5a288a4ce92b190577cb472157bb.png.webp) 对应数据字段分析如下: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/3cded2a56f84c865e2dfb5e50bc323c0.png.webp) `System Volume Information` 目录下存在两个文件,分别是`IndexerVolumeGuid` 和 `WPSettings.dat`。根据上述分析可知: + `IndexerVolumeGuid`文件的数据存放在 FAT[3],3号簇上,即数据区的第3个簇(数据区的第1个簇为2号簇); + `WPSettings.dat` 文件的数据存放在 FAT[4],4号簇上,即数据区的第2个簇(数据区的第1个簇为2号簇); --- 首先,我们跳转到4号簇上查看`IndexerVolumeGuid`的数据,对应地址计算方式为: `FirstSectorofCluster` = `DataStartSector` + (N - 2) * `BPB_SecPerClus` = 512 + (4 - 2) * 4 = 520; 对应地址为: `FirstSectorofCluster` * `BPB_BytsPerSec` = 520 * 512 = 0x0004 1000 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/04d8392cdae5d5d2db6e4b71bb9f7790.png.webp) --- 接着跳转到3号簇上查看`WPSettings.dat`的数据,对应地址计算方式为: `FirstSectorofCluster` = `DataStartSector` + (N - 2) * `BPB_SecPerClus` = 512 + (3 - 2) * 4 = 516; 对应地址为: `FirstSectorofCluster` * `BPB_BytsPerSec` = 520 * 512 = 0x0004 0800 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/42db922320ba147f4c040efcbb744fbf.png.webp) ### 5.5 新增文件测试 1. 在根目录下新增 `test` 目录,使用winhex更新磁盘数据,观察各数据区变化 + 保留区无变化 + FAT区变化如下: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/a81314c53bbafcf50625f57755e8d40d.png) ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/266f366c061de53e0505290153cca365.png) + 根目录区变化如下: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/20135c3812108915820768256b405371.png.webp) + 数据区变化: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/859af6bd4880792d6c04d8b57d10ee0b.png.webp) 2. 新增`long file test`文件夹,里面存入一个 ==长度为 2050 Byte(占据两个簇的空间)== 的`test.txt`文件,使用winhex重新打开磁盘进行分析。 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/b18a237ccdc5f2ddf3ab74309d167dd5.png) + 保留区无变化 + FAT区变化如下: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/f82b809fcdb6ca83d9321f730cb64fcd.png.webp) ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/daaf627bf2e99f63939330229bc8e545.png.webp) + 根目录区变化如下: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/94f3979f885c2ae327538bd409085eaa.png.webp) + 数据区变化如下: `long file test` 目录数据指向6号簇,跳转至6号簇,地址 `DataStartSector + (N - 2) * BPB_SecPerClus = 0x40000 + (6-2) * 4 * 512 = 0x420000` ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/eddceb911888619aa3d187cbacbb05f0.png.webp) `test.txt` 文件指向 7号簇,跳转至7号簇,地址 `DataStartSector + (N - 2) * BPB_SecPerClus = 0x40000 + (7-2) * 4 * 512 = 0x428000`,均为`test.txt`的实际有效数据,如下: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/20478875871a0d22c6221d52049dcb3c.png) ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230604/538d059b2981e423856b56bbb7fe7053.png) ## 6. 总结 以上便是关于FAT文件系统的全部分析了,通过上述分析,外加新增文件辅助理解,对于文件在FAT文件系统下如何管理、存储,相信已经有了非常深入的了解。 FAT文件系统分为四个区: 1. 保留区最重要的是里面包含引导扇区,引导扇区内存放着BIOS参数信息,通过此参数可以知道FAT文件系统的flash布局,以及flash大小,fat块大小、簇大小等关键信息; 2. FAT区,记录了文件所占用簇的情况,以及对于文件大小大于一个簇的文件,在FAT区内形成簇链,记录文件由哪几个簇组成 3. 根目录区,只有FAT12/16系统所有,记录了根目录下的文件/目录条目信息 4. 数据区,记录数据分为两个部分,第一部分为目录信息,除根目录外,每个文件夹需要占据一个及以上的簇描述对应目录下的文件情况;第二部分为具体文件数据。两部分数据通过短文件名SFN字段进行关联! 以上就是FAT文件系统的简单概括,由于本文使用的是FAT16文件系统作为实例分析,关于FAT32文件系统,在下一篇博文中进行补充,链接如下:**[FAT32文件系统详解(点击跳转!!!)](https://blog.csdn.net/qq_43332314/article/details/131019758)** 关于FAT文件系统或者本文有关问题,欢迎大家在评论区讨论! --- **创作不易,转载请注明出处!** **关注、点赞+收藏,可快速查收博主有关分享!** ---- + 下一篇:**[FAT32文件系统详解(点击跳转!!!)](https://blog.csdn.net/qq_43332314/article/details/131019758)** + 本人博客主页:[**`爱出名的狗腿子(点击跳转)`**](https://blog.csdn.net/qq_43332314?spm=1011.2421.3001.5343)
4
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
jaffer
You can contact me by Email, jaffer.work@foxmail.com, if you want.
文章
4
回答
10
被采纳
0
关注TA
发私信
相关文章
1
用sd+usb_mass+elm_fat实现了一个512MU盘,传输速度很慢
2
easyflash存放于fat文件系统中,但是会擦除文件系统
3
fatdisk 生成好的fat文件系统怎么烧写到外部的flash中呢?
4
日志存储是使用fat如何做磨损平衡?
5
spi flash 可以通过挂载 fat 和 easyflash 吗?
6
W25Q64的文件系统如何挂载
7
elmfat文件数有限制,该修改哪里?
8
stm32f429 把片内flash挂载到文件系统上时不成功
9
STM32F407ZG利用片上 flash运行fal+dfs文件系统,扇区128KB,加载和格式化报错
10
使用STM32F4内置Flash构建虚拟文件系统时,Flash设备时扇区大小应该如何定义?
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
PWM
cubemx
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
xusiwei1236
8
个答案
2
次被采纳
踩姑娘的小蘑菇
1
个答案
2
次被采纳
用户名由3_15位
7
个答案
1
次被采纳
bernard
4
个答案
1
次被采纳
RTT_逍遥
3
个答案
1
次被采纳
本月文章贡献
聚散无由
2
篇文章
15
次点赞
catcatbing
2
篇文章
5
次点赞
Wade
2
篇文章
3
次点赞
Ghost_Girls
1
篇文章
6
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部