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部分,来索引出幸存文件了,这就是数据恢复软件的原理。
FAT32文件和目录的组织方式相关推荐
- 在NIO.2中使用文件和目录
在先前的文章中,我讨论了文件和目录的创建( 创建文件和目录 )以及选择( 列出和过滤目录内容 ). 采取的最后一个合乎逻辑的步骤是探索我们如何使用它们以及如何使用它们. 这是库的一部分,它经过了重新设 ...
- 【学习笔记】第四章——文件 I(文件管理、逻辑结构、目录与文件分配方式)
文章目录 一. 初识文件管理 1)文件的属性 2)向上提供的功能 二. 逻辑结构与目录 1)顺序文件 2) 目录 三. 文件分配方式 连续分配 链式分配 索引分配 一. 初识文件管理 1)文件的属性 ...
- FAT,FAT32,NTFS单目录文件数量限制
FAT32FAT32 标称为65534,实际到2万+时已不稳定. NTFS 似乎没有明确限制单目录文件数量,但有人反应在生成10万+ 文件的目录时遇到报错,想来应该是和文件属性(文件名等).磁盘使用状 ...
- 【C、C++、Windows API、 boost】多种方式判断文件、目录是否存在
转载本文是为了实际之需要,方便查阅. 一.判断文件是否存在 #ifdef WIN32 #include <io.h> //C (Windows) access #else #include ...
- Git:add多个文件或者目录的方式
git add 多个文件和文件夹的方法 http://www.360doc.com/content/20/0918/19/1314937_936434911.shtml git add 文件 方法一 ...
- 修复移动硬盘“文件或目录损坏且无法读取”错误
昨天在用移动硬盘下载文件时忽然出错,以为是小错误并没在意,直接关机拔掉了硬盘.今天再连到电脑上时,发现硬盘无法读取,XP提示"文件或目录损坏且无法读取".换了台电脑试了下,问题依旧 ...
- 保留扇区读写java,FAT32文件操作系统
FAT32文件系统 FAT32文件系统的存储机制及其在单片机上的实现based on a SD card. FAT32文件系统您一定不会陌生,最多看到它是在windows操作系统里,但在一些嵌入式产品 ...
- 路径,文件,目录,I/O常见操作汇总
摘要: 文件操作是程序中非常基础和重要的内容,而路径.文件.目录以及I/O都是在进行文件操作时的常见主题,这里想把这些常见的问题作个总结,对于每个问题,尽量提供一些解决方案,即使没有你想要的答案 ...
- Linux指令--文件和目录属性
对于每一个Linux学习者来说,了解Linux文件系统的目录结构,是学好Linux的至关重要的一步.,深入了解linux文件目录结构的标准和每个目录的详细功能,对于我们用好linux系统只管重要,下面 ...
最新文章
- Drawer的使用(一):xml文件中使用
- DAY4-打卡第四天-2018-1-12
- Linux7的ftp日志怎么看,centos7打开sftp操作日志
- [Leedcode][JAVA][第914题][最大公约数]
- CentOS7配置rsync实现文件同步
- nginx+keepalived构建主备负载均衡代理服务器
- 交流电机数字控制系统_干货 | 简述伺服电机和步进电机的六大性能差异
- 史上最全java自动化测试工具汇总
- 分镜头剧本模板、故事图模板
- JAVACC使用总结(四):LOOKAHEAD解决语法选择冲突的利刃
- Linux HA Cluster的实例演示(2)
- 哈啰:学拼多多的套路,走美团的老路
- 安装vue脚手架出现的问题 npm ERR! code EEXIST。。。
- 服务器运维KPI指标,IT运维包括哪些内容,考核标准是什么
- 登录成功后怎么跳转页面html,怎么设置登录成功后跳转到相应的页面
- vs2017配置linux连接器失败,Xilinx HLx 2017.1与VS 2017兼容问题解决的方法盘点
- 基于Halcon学习的二维码识别【一】micro_qr_simple.hdev
- 计算机书籍旧版好还是新版好,新固件还不如老版本好用?教你如何禁止Kindle自动更新!...
- 团队交流合作简单解决方案:TeamViewer远程控制会议演示 + HyperCam屏幕录制(免费)...
- Servlet 深度了解 JSPJava编程
热门文章
- 风波之后,天鸽互动回购股票“补刀”沽空机构
- Jupyter notebook不自动弹出网页解决办法
- 定义一个Ladder类用来刻画“梯形”,要求:Ladder类具有类型为double的上底、下底、高、面积属性,具有返回面积的功能。
- js获取android数据,Android webview与js的数据交互
- FFmpeg源码分析:写媒体文件头avformat_write_header()
- [C++/Learning] 基于SMO的非线性支持向量机(SVM)可视化程序(附代码)
- csp 201609-3 	炉石传说
- 一锅大杂烩-------板子合集
- 从微软网站下载Windows10官方ISO镜像
- Dockerfile实例部署和测试