经典FAT文件系统格式详解
FAT/FAT32曾经是windows下主流文件格式,虽然FAT已经这么多年了,也见识到一些缺点,但熟悉FAT,还是对文件系统认识有很大帮助。
一般来说,需要具备一些前期知识:
1. 文件存在flash或硬盘里,还是一个个字节进行存储的,存储介子本身不负责具体内容。如果要对硬盘的数据进行识别,必须需要一定格式,还需要一个驱动程序进行识别。
2. 文件格式最主要作用:格式化管理,快速查找文件
还有几个知识点:
扇区:一般为512,最小分割单位,但不是最小管理单位。
簇:管理最小单位,一般是由多个扇区组成,有2、4、8、16、32或64几种情况。如果=16,即8K=16*512,也就是说一个文件最小也得占8K空间
DBR及保留扇区(FAT16不存在)、文件分配表区(FAT1、FAT2)、数据区(DATA区)。
反过来说,一个文件至少占一族,或多簇。最后一簇往往是未存满的,这样肯定会带来空间的浪费。(但也没太好办法,毕竟是要快速查找文件的)
当然,这个可以根据实际情况来设定,毕竟应用场景不一样。
FAT16的根目录区只有32个扇区,计算一下,每个扇区512字节,共32个扇区,而每个文件名至少要占用32个字节(后面会介绍),很显然,根目录最多只能放512个文件了。
FAT16 这里存在致命缺陷的:
例如:我们的文件是存在根目录的,那么最多也只能存放512个文件(假设文件名又很长的话,这个数量就更小了)
FAT32对这方面进行了优化,可以支持更多的文件数量,但格式变得复杂了,查找时也是先从前面扇区往后跳(会根据目录最后指向下一个扇区地址),所以时间上也浪费很多,搜索起来非常慢
FAT大体格式:
这里:FAT表有两个,一般FAT2为镜像。FAT表非常重要,一般如果文件名乱码,或一下子消失了,往往是FAT表坏掉了。
MBR: Main Boot Record 主引导记录区
DBR:Dos Boot Record 操作系统引导记录区
以下,是我对FAT16格式注解:
FAT格式:(FAT16)
FAT32格式:
FAT表格式:
FAT表之后就是目录区:
目录区是由一个个目录项构成,类似于FAT表。其中每一个目录项占用32个字节,可以是代表长文件名目录项、文件目录项、子目录项等。对于短文件名格式的目录项,其参数的含义如表1所示(不会画这种表,从别处引用了一个)[1]:
通过这些信息,也基本知道文件信息,比如创建信息,修改信息等
也可以看到有两个限制:
1. 文件名的长度为8字节,那长一点文件名怎么办?
2.扩展名为3字节,这个虽然是限制,但实际还可以接受。
其实,这个历史原因导致的,原先Dos系统下就根本没考虑中文这种场景(现在windows系统都是基于DOS发展起来)
那长一点文件名怎么办?
先看下FAT32对长文件名的处理:
表15 FAT32长文件目录项32个字节的表示定义 |
||||
字节偏移 |
字节数 |
定义 |
||
0x0 |
1 |
属性字节位意义 |
7 |
保留未用 |
6 |
1表示长文件最后一个目录项 |
|||
5 |
保留未用 |
|||
4 |
顺序号数值 |
|||
3 |
||||
2 |
||||
1 |
||||
0 |
||||
0x1~0xA |
10 |
长文件名unicode码① |
||
0xB |
1 |
长文件名目录项标志,取值0FH |
||
0xC |
1 |
系统保留 |
||
0xD |
1 |
校验值(根据短文件名计算得出) |
||
0xE~0x19 |
12 |
长文件名unicode码② |
||
0x1A~0x1B |
2 |
文件起始簇号(目前常置0) |
||
0x1C~0x1F |
4 |
长文件名unicode码③ |
- 0x00~0x00:1 个字节,长文件名目录项的序列号,一个文件的第一个目录项序列号为 1,然后依次递增。如果是该文件的最后一个长文件名目录项,则将该目录项的序号与 0x40 进行“或(OR)运算”的结果写入该位置。如果该长文件名目录项对应的文件或子目录被删除,则将该字节设置成删除标志0xE5。
- 0x01~0x0A:10 个字节,长文件名的第 1~5 个字符。长文件名使用 Unicode 码,每个字符需要两个字节的空间。如果文件名结束但还有未使用的字节,则会在文件名后先填充两个字节的“00”,然后开始使用 0xFF 填充。
- 0x0B~0x0B:1 个字节,长目录项的属性标志,一定是 0x0F。
- 0x0C~0x0C:保留。
- 0x0D~0x0D:1 个字节,校验和。如果一个文件的长文件名需要几个长文件名目录项进行存储,则这些长文件名目录项具有相同的校验和。
- 0x0E~0x19:12 个字节,文件名的第 6~11 个字符,未使用的字节用 0xFF 填充。
- 0x1A~0x1B:2 个字节,保留。
- 0x1C~0x1F:4 个字节,文件名的第 12~13 个字符,未使用的字节用 0xFF 填充。
总共这个也是32字节(跟短文件名一致),那么如何区别?关键看0B位置,如果是=0F,那么就是长文件名。
长文件名的实现有赖于目录项偏移为0xB的属性字节,当此字节的属性为:只读、隐藏、系统、卷标,即其值为0FH时,DOS和WIN32会认为其不合法而忽略其存在。这正是长文件名存在的依据。
长文件名中的字符采用unicode形式编码(一个巨大的进步哦),每个字符占据2字节的空间。
从上表中可以获得文件名长度:0x01~0x0A:10字节; + 0x0E~0x19 12字节; + 0x1C~0x1F 4字节;
总共26字节,即13个字符!(被拆七零八落,有点可怜)
那么怎么表达一个长文件名呢?从以上两个表来看,都无法做到兼容
解决办法:短文件名(32)+长文件名(32)....
规则:
(1)、取长文件名的前6个字符(第1个字符改成0x01)加上"~1"形成短文件名,扩展名不变。(放在第1组32字节)
(2)、如果已存在这个文件名,则符号"~"后的数字递增,直到5。(如果是看二进制是可以看到这些奇怪的名称,有时候在文件fat表破坏掉之后也会出来~1这样的奇怪文件)
(3)、如果文件名中"~"后面的数字达到5,则短文件名只使用长文件名的前两个字母。通过数学操纵长文件名的剩余字母生成短文件名的后四个字母,然后加后缀"~1"直到最后(如果有必要,或是其他数字以避免重复的文件名)。
(4)、如果存在老OS或程序无法读取的字符,换以"_"
(5)、从第2组32字节开始,以13字节为一组,分别表示的文件名。如果存在多组,则以倒叙的办法表示,前面的是文件名末尾部分。
有点懵,举个例子说明下更清晰:
假设一个文件,文件名为 ZHUANCHU-12345678-20190910H095830.bin
文件名实际长度=33,那么/13,需要三组长文件名来表示。
3组 | H095830 不足部分用FF替代 |
2组 | 5678-20190910 |
1组 | ZHUANCHU-1234 |
这个格式参考上面的 《长文件名格式》
那么FAT格式文件名大概是这样的:
其中0F表示长文件名的标志。
96表示短文件名校验值(三组都一样)
红色框起来的是文件名的分组。根据长文件名格式定义,被拆分成不同地方。
好了。我们再回到文章前面的,计算下,如果我们的文件名大概都是这样的:“ZHUANCHU-12345678-20190910H095830.bin”,只是数字不一样
那么FAT根目录下最多可以存放多少个这样的文件?(假设全部文件容量要大大小于存储空间)
一个文件名占 3长+1短,共4个坑。总共512个坑,那最多只能放128这样的文件!
这也是,为什么我们存储空间是够的,但存储是失败的原因~~
so,根目录的文件名可不是随意的,建议小于8字节最佳!(这样只要一组短文件名就可以了)~~
搞定了文件名的含义,我们再来看看文件内容存放(实际就是关注起始地址)
文件起始地址
文件起始地址(文件内容) = (保留扇区数 + FAT表扇区数 * FAT表个数(2) + (文件起始簇号-2)*每簇扇区数)*每扇区字节数
文件起始簇号:(在短文件名里)高在前
实际计算:感觉文件起始簇号不用-2,OK?
例如下列文件 实验1:
数据区偏移地址:ADDR=(8+256*2+(0x09C3 – 2)*16)* 512= 20,721,664=0x013C3000
根目录有 32*512=0x4000 (固定32个扇区表示目录,内容区)
实际地址=0x013C3000+0x4000=0x013C7000
实验2:
假设 rec/文件夹有一个文件为:888888.txt 里面内容为 “jjw”
来分析下,子文件/rec下一个888888.txt文件(实际跟放在哪里没有关系)
数据区偏移地址:ADDR=(8+256*2+(0x09C4 – 2)*16)* 512= 20,729,856= 0x013C5000
实际地址=0x013C5000+0x4000=0x013C9000
再来说下,时间信息格式:
日期时间,可以表示创建信息,修改信息,访问信息
格式都一样,这里以创建信息举例:
0x0E~0x0F |
文件创建时间 |
0x785C = (0111100001011100)(2进制) 即为 15:02:57(注释1) |
0x10~0x11 |
文件创建日期 |
0x4508 = (0100010100001000)(2进制) 即为 2014/8/8(注释2) |
注释1:01111 000010 11100
1)这里高5位代表小时,由于2^5 = 32,足够表示24小时,这边01111(2进制) = 15(10进制);
2)次6位代表分钟,同理2^6 = 64,足够表示60分钟,这边000010(2进制) = 2;
3)低5位表示秒的1/2, 计算结果需要加上毫秒位上的进位,这边11100(2进制) = 28(10进制),所以秒数 = 28*2 = 56,再加上毫秒上的进位1所以结果为57。
注释2:0100010 1000 01000
1)这里高7位代表从1980年开始的年数,笔者计算了下可以到2108年,总之还有90多年可以使用,这边0100010(2进制) = 34,所以年份 = 1980+34 = 2014;
2)次4位代表月份,2^4=16,可以表示12个月份,这边 1000(2进制) = 8(10进制);
3)低5位代表日期,2^5 = 32,可以表示28~31天,这边 01000(2进制) = 8(10进制)。
这些信息,都蛮奇怪了。不过经常接触嵌入式,也就不奇怪了。
经典FAT文件系统格式详解相关推荐
- 文件系统的详解与常见文件系统模式比较
目录 1. 文件系统的详解 2. 常见的文件系统比较 2.1 Windows下的文件系统 2.1.1 FAT16文件系统 2.1.2 FAT32文件系统 2.1.3 FAT64文件系统(exFAT文件 ...
- Java字节码(.class文件)格式详解(一)
原文链接:http://www.blogjava.net/DLevin/archive/2011/09/05/358033.html 小介:去年在读<深入解析JVM>的时候写的,记得当时还 ...
- php serialize取值,PHP 序列化(serialize)格式详解
PHP 序列化(serialize)格式详解(转) 1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PH ...
- php serialize mysql_php 序列化(serialize)格式详解
1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PHP 手册中对这两个函数的说明仅限于如何使用,而对序列 ...
- 三维重建:PNG格式详解-与LibPNG使用
PNG图像包含了骨骼信息,左边的图像比右边的大几十K,包含了骨骼信息: PNG格式详解:https://blog.mythsman.com/post/5d2d62b4a2005d7404 ...
- 4-4:TCP协议之TCP头部格式详解
文章目录 一:TCP头部格式详解 (1)4位首部长度 (2)序列号和确认应答号 A:可靠性问题 B:32位序号和确认号 (3)窗口大小 (4)标志位 (5)紧急指针 A:带外数据(out_of _ba ...
- PHP 序列化(serialize)格式详解
1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PHP 手册中对这两个函数的说明仅限于如何使用,而对 ...
- 安卓camera2 API获取YUV420_888格式详解
安卓音视频开发中的一个环节是摄像头采集数据,Android平台上摄像头采集的API有两套,camera1和camera2.本文主要讲的是camera2这套API采集数据,并指明YUV420_888格式 ...
- Gerber 格式详解
Gerber 格式详解 gerber中文 gerber,gerber 文件:590m.com/f/25127180-487459253-79168e(访问密码:551685) 以下内容无关: ---- ...
- BMP格式详解<转>
BMP格式详解 BMP文件格式详解(BMP file format) BMP文件格式,又称为Bitmap(位图)或是DIB(Device-Independent Device,设备无关位图),是Win ...
最新文章
- VB6 通过winsock控件数组实现客户端和服务器多对一通信
- 17章 SPI控制器(XIlinx ZYNQ-7000 SOC UG-585文档)
- hibernate的lazy的使用
- centOS7挂在windows移动硬盘方法
- 易语言神经网络验证码识别_递归神经网络 GRU+CTC+CNN 教会验证码识别
- django-自定义转换器-实操案例
- php软件开发--nodejs
- [DOTween]使用过程中的一些注意事项记录
- Weld(CDI)教程
- 抖音c语言表白编码,抖音微信表白代码大全 微信表白代码总汇
- manul_css.css:1 Failed to load resource: the server responded with a status of 404 ()
- linux ps 2鼠标驱动,佳能 PS/2 TrackPoint 驱动程序下载-更新佳能软件(鼠标)
- ATtiny85简单引脚配置
- 使用Teamviewer实现远程控制安卓设备的实现过程记录
- 不同终端通信用c语言实现,采用蓝牙技术的北斗终端通信模块的设计
- Excel合并计算完成多表格数据汇总求和
- 2022ICPC预选赛 A Yet Another Remainder(数学)(构造)
- IDEA 显示Cannot resolve plugin org.apache.maven.pluginsmaven-site-plugin3.3
- MATLAB 绘制时钟(同步当前时间)
- SpringBoot中重试框架——Spring-retry与Guava-Retry