文件系统到底是个啥?单凭一句话来解释清楚这个问题可不容易。
所以我决定写下这篇文章来聊这个话题。写这篇帖子的初衷是想从一个比较宏观的角度去谈各种文件系统,但文中时不时也会提及一些微观层面的概念,但愿你读到时不会睡着。 :)
什么是文件系统?
先来上一个简单的定义:
文件系统 决定着从存储设备中对文件进行 命名、 存储 和 检索 的方式。
当说到“文件系统”一词时,基于不同的语境,人们实际所指的可能是有关“文件系统”一词多重定义的某个方面 - 这也是让问题变得棘手的地方。
最后,你可能会“扪心自问”,文件系统到底是个啥? 🤯
在本份指南,我将帮你理解这一问题,并助你搞定任何关于文件系统的谈话。此外,为了帮助你理解有关文件系统的一些概念,指南也会涉及分区和启动程式的讲解。
为保证指南的可操作性,在解释较低级别的结构或控制台命令时,我将专注Unix一类的环境下进行讲解。虽说如此,讲到的概念和其它环境及文件系统也是相通的。
你可能有这样一个疑问,我们在最初为什么需要文件系统?
嗯,答案是,如果没有文件系统,存储设备会将大量数据简单地堆积存储,如此一来数据间便无法区分。
文件系统的命名源于过去的纸质数据管理系统。纸质系统中,我们将文档保留为纸质文件,然后将其存放在各个目录中。
试想如若没有分类,存放文件的房间便到处都会堆着杂乱无章的文件。
类似地,当存储设备缺少了文件系统时便会陷入同样的杂乱无序之中,存储设备本身也将毫无用处。
然而,有了文件系统之后,一切便会全然不同:
但也不能说文件系统的功能仅限于整理数据。
空间管理,元数据,数据加密,文件访问控制和数据完整性同样是文件系统施展拳脚的阵地。
一切始于分区
首次使用之前,必须对存储设备进行 分区 and 格式化。
什么是分区?
分区是指将存储设备划分为几个 逻辑区域 的过程。分区后,就能像管理一个个彼此独立的存储设备一样对这些逻辑区域进行一一单独管理。
分区过程可通过操作系统提供的磁盘管理工具完成,亦或通过系统固件提供的基于文本\的CLI工具完成。
一台存储设备应至少有一个分区,视需求而定,也可进行更多分区。
举例来说,一个基础的Linux安装就具有三个分区:一个专门用于存储操作系统,一个专门用于存放用户文件,外加一个交换分区。
在Windows和Mac OS系统中并没有专门的交换分区,它们在操作系统所安装的分区内管理交换,但二者的分区布局与Linux相似。
那么我们又为什么要把存储设备进行分区呢?
原因在于,我们不想将整个存储空间作为单个单元或出于单个目的进行管理和使用。
这和我们划分工作空间的理念类似,你不也会把办公空间划分为会客室、会议室、团队办公区域吗?
在具有多个分区的计算机上,你可以安装多种操作系统,并且每次可以选择不同的分区来启动系统。
就连恢复和诊断实用程序也有它们的专属分区。
比方说你要在恢复模式下重新启动MacBook,你就需要在重启或启动MacBook后迅速按住 Command + R
不松。
这样一来,你就在指示操作系统使用包含恢复程序的分区启动MacBook。
然而,分区的意义不仅仅限于可安装多种操作系统和工具。通过分区,我们还可以将重要系统文件与普通文件分开存放。
这样一来,无论你在计算机上安装了多少个大型游戏,都不会对操作系统的性能产生任何影响, - 因为它们和系统文件存放在不同的分区中。
再拿办公室那个例子来说,如果将一个呼叫中心团队和一个技术团队安排在同一个办公区域,那么如此对两个团队的生产力产生的影响都会是负面的,因为这两个团队都有自己独特的效率需求。
好比说,技术团队会更需要一个安静的工作环境。
有一些操作系统,比如Windows,会对磁盘分区分配以不同的字母编号(A,B,C,D)。例如,Windows上 主分区 (Windows所安装的分区)的命名为 C:或驱动器C。
而在Unix一类的操作系统中,分区则以根目录下的普通目录显示,这点在后面会介绍到。
至于现在吗,我们要先绕个弯 ↩️
在下一小节,我们将更深入地理解分区,内容涉及 系统固件 and 启动程式 这两个概念,它们将改变你对文件系统的看法。
准备好了吗?
一起看看吧! 🏊♂️
分区方案,系统固件和启动程式
在对存储设备进行分区时,有两种分区方法可供选择:
- 主引导记录(MBR)方案
- GUID分区表(GPT)方案
无论选择哪种方案,存储设备上的前几个存储块所存储的始终都是有关分区的关键数据。
利用这些数据结构,系统的 固件 便能启动操作系统。
等等,你可能会问,啥又是系统固件?
解释如下:
固件是嵌入电子设备中以操作该设备或引导另一个程序来操作该设备的低级软件。
固件存在于计算机,外围设备(键盘,鼠标和打印机)中,甚至存在于家用电器中。
在计算机中,固件为诸如操作系统之类的复杂软件提供了启动和使用硬件组件的标准环境。
但是,在打印机等较为简单的系统上,固件却是设备的主要操作系统。打印机菜单就是其固件的人机界面。
计算机固件依据以下两个规范执行:
- 基本输入/输出(BIOS)
- 统一可扩展固件接口(UEFI)
固件(或基于BIOS或基于UEFI)存储在非易失性存储器中,例如连接到主板的Flash ROM(闪存)。
BIOS,图源: Thomas Bresson, 许可: CC BY 2.0
当打开计算机电源时,固件是第一个运行的程序。
固件的任务(除其它事项外)包括启动计算机,运行操作系统,并将整个系统的控制权传递给操作系统。
固件还可以(在连网环境下)运行预操作系统,例如恢复或诊断工具,甚至还可以运行特殊的壳层来运行基于文本的命令。
在操作系统的徽标出现之前,你所看到的那几个页面就是计算机固件的输出,该输出用于验证硬件组件和内存的运行状况。
初始检查完成后会发出“哔”的一声,表明一切正常。
MBR分区方案是BIOS规范的一部分,由基于BIOS的固件使用。
在采用MBR分区方案的磁盘上,存储设备上的第一个扇区存储着启动系统所需的基本数据。
这一扇区被称为MBR(主引导扇区)。
MBR内存有以下信息:
- 引导程序,它是(机器代码中的)一个 简单程序,用于启动引导过程的第一阶段
- 分区表,其中包含有关分区的信息。
MBR分区磁盘上,基于BIOS的固件与基于UEFI的固件会以不同的方式引导系统。
以下是其工作过程:
系统启动后,BIOS固件将启动并将MBR的内容加载到内存中,并在其中运行引导加载程序。
通过将引导加载程序和分区表放置在MBR之类的预定义位置中,便可使BIOS来引导系统,而无需处理任何文件。
MBR中的引导加载程序代码占用MBR 512字节空间中的434字节至446字节,另有64字节分配给了分区表,分区数量最多为四个。
446字节并不足以容纳很多的代码。正因如此,复杂的引导加载程序(例如Linux上的GRUB 2)会将其功能切分为多个部分或多个阶段。
其中,最小的部分被称为第一阶段引导加载程序,位于MBR内。
第一阶段引导加载程序将启动引导过程的下一个阶段。
MBR之后紧接第一个分区之前,还有一个很小的空间,大约1MB,被称为 MBR间隙。必要时,它也可以用来放置一部分引导加载程序。
利用MBR间隙,引导加载程序(例如GRUB 2)存储其功能的另一阶段。GRUB将此称为引导加载程序的 第1.5个阶段 ,其中包含文件系统驱动程序。
1.5阶段使GRUB的下一阶段可以处理文件,而不再是再向第一阶段引导加载程序那样从存储设备中加载原始数据。
第二阶段引导加载程序,现在为file-system-aware,可以加载操作系统的引导加载程序文件来引导操作系统。
此时也是操作系统徽标逐渐淡出的时候...
下面是MBR分区存储设备的图示:
如果我们放大MBR这部分,其内容将显示如下:
尽管MBR非常简单并得到了广泛兼容,但它仍存在一些局限。
MBR的数据结构将分区数量限制为了仅 四个主要 分区。
一个常见的解决方法是在主分区旁创建一个 拓展 分区。总之,只要分区总数不超过四个即可。
扩展分区又可以分为多个 逻辑分区。
进行分区时,你可以在主分区和扩展分区之间进行选择。
解决此问题后,我们还面临着第二个限制。
每个分区的空间最大为2TiB。
等等,这还不只!
MBR扇区的内容还没有备份 😱,也就是说一旦MBR遭到意外损坏,我们的存储设备就变成废铁了。
比起MBR, GPT 分区方案要更复杂,但没有MBR那些限制。
比如,只要操作系统允许,你可以拥有尽可能多的分区。
而且每一个分区的大小都可以达到市场上最大的存储设备的大小, - 实际上还可以更大。
GPT正在逐步取代MBR,尽管旧PC和新PC仍广泛支持MBR。
如前所述,GPT是UEFI规范的一部分,该规范正在替代旧的BIOS。
这意味着基于UEFI的固件将使用GPT分区的存储设备来执行引导。
现在,许多固件和操作系统都支持UEFI,并使用GPT方案对存储设备进行分区。
在GPT分区方案下,出于与基于BIOS\的系统兼容的考虑,保留了存储设备的第一个扇区。这是因为某些系统可能仍使用基于BIOS\的固件,但却具有GPT\分区的存储设备。
这一扇区被称为 保护性MBR。 (这也是第一阶段引导加载程序在MBR\分区的磁盘中存储的位置)
第一个扇区之后,将存储GPT数据结构,包括 GPT标头 和 分区表条目。
作为备份,GPT条目和GPT标头也会存储在存储设备的最后,这样即便主副本被损坏,也可以将其恢复。此备份被称为 辅助GPT。
下面是GPT分区存储设备的图示:
GUID Partition Table Scheme ,图源: Kbolino, 许可: CC BY-SA 2.5
在GPT中,所有的引导服务(引导加载程序,引导管理器,预操作系统环境和壳层)都位于名为 **EFI系统分区(简称ESP)**的特殊分区中,UEFI固件可以使用该分区。
ESP甚至拥有自己专属的文件系统,该系统是 FAT的一个特定版本。在Linux上,ESP存储在 /sys/firmware/efi
路径下。
如果你在自己的系统上找不到此路径,那你的固件可能是基于BIOS的固件。
想要查看的话,你可以尝试将目录更改为ESP挂载点,如下所示:
cd /sys/firmware/efi
基于UEFI\的固件会假定其存储设备使用GPT进行分区,并在GPT分区表中查找ESP。
找到EFI分区后,该固件将查找已配置的引导加载程序,该引导加载程序通常是以 .efi
结尾的文件。
基于UEFI\的固件从 NVRAM (一种非易失性RAM)中获取引导配置。NVRAM内有引导设置以及前往操作系统引导加载程序的路径。
如果进行了相应的配置,UEFI固件也能够执行BIOS-boot,进而从MBR磁盘启动系统。
在Linux上,你可以使用 parted
命令来查看用于存储设备的分区方案。
sudo parted -l
命令输出如下:
型号:Virtio Block设备(virtblk)
磁盘 /dev/vda: 172GB
扇区大小(逻辑/物理): 512B/512B
分区表: gpt
磁盘标志:
编号 开始 结束 大小 文件系统 名称 标志
14 1049kB 5243kB 4194kB bios_grub
15 5243kB 116MB 111MB fat32 msftdata
1 116MB 172GB 172GB ext4
根据上面的输出可知,示例存储设备的ID为 /dev/vda
,容量为172GB。
该存储设备基于GPT进行分区,共具有三个分区,其中第二和第三分区分别基于FAT32和EXT4文件系统进行格式化。
该固件具有BIOS GRUB分区,表明其为基于BIOS的固件。
让我们借助 dmidecode
命令来确认这一点,输入如下:
sudo dmidecode -t 0
输出结果如下:
# dmidecode 3.2
Getting SMBIOS data from sysfs.
SMBIOS 2.4 present.
...
✅ 我们的判断没错!
完成分区后,就要对分区进行格式化了。
大多数操作系统都支持基于一组文件系统来格式化分区。
例如,在Windows上格式化分区时,可以选择 FAT32, NTFS 或 exFAT。
格式化还会涉及创建各种 数据结构 和用于管理分区内文件的元数据。
这些数据结构便是文件系统诸多定义中的一个方面。
我们拿NTFS文件系统为例吧。
在将一个分区格式化为NTFS时,格式化过程会将关键NTFS数据结构以及 **主引导表(MFT)**放置在该分区上。
好了,关于分区和启动已经讲得够多了,我们还是绕回到文件系统吧。
文件系统一开始是怎么出现的,如今又发展到了什么地步
文件系统由一套数据结构、连接电路、抽象概念和API构成。它们协同工作,步调一致地管理任何类型存储设备上的任何类型文件。
每种操作系统会使用特定的文件系统来管理文件。
过去,Microsoft曾在 MS-DOS 和 Windows 9x 家族系统中使用过 FAT (FAT12,FAT16和FAT32)。
但自Windows NT 3.1 开始,Microsoft研发了 新技术文件系统(NTFS)。比起FAT32,NTFS具有诸多多优势,如支持更大的文件,更长的文件名,数据加密,访问管理,日志记录等等。
从那时起,NTFS就一直是Window NT家族(2000,XP,Vista,7、10等)的默认文件系统。
不过,NTFS并不适用于非Windows环境。
例如,在Mac OS上,你 只能读取 NTFS格式存储设备(如闪存)上的内容,但无法写入任何内容,除非安装具有写入支持的NTFS驱动程序。
2006年,Microsoft创建 扩展文件分配表(exFAT) 文件系统,exFAT堪称NTFS的精简版。
exFAT的设计面向对象是大容量可移动设备(例如外部硬盘,USB驱动器和存储卡)。
它也是 SDXC 卡 使用的默认文件系统。
与NTFS不同,exFAT在非Windows环境(包括Mac OS)上也支持 读写 ,这也使其成为最佳的高容量可移动存储设备跨平台文件系统。
因此基本上可以这么说,如果你想同时在Windows、Mac和Linux上使用同一块可移动磁盘,就需要将其格式化为exFAT格式。
多年以来,Apple也在研发利用自己的各种文件系统,这就包括
分层文件系统(HFS), HFS+ 以及最近推出的 苹果文件系统(APFS).
和NTFS类似,APFS也是一个日志文件系统。自苹果在2017年推出 OS X High Sierra 以来,APFS一直使用至今。
文件系统中的 扩展文件系统(ext) 家族是专门为Linux内核(即Linux操作系统的核心)创建的。
ext 的第一版发布于1991年,但不久便在1993年被 第二代扩展文件系统 (ext2) 取代。
进入21世纪,针对Linux开发的具有日志功能的 第三代扩展文件系统 (ext3) 和 第四代扩展文件系统 (ext4) 也相继出现。
如今,ext4 成为Linux的许多发行版本中的默认文件系统,这就包括 Debian 和 Ubuntu。
在Linux上,你可以输入 findmnt
命令来陈列出ext4\格式的分区:
findmnt -lo source,target,fstype,used -t ext4
输出结果如下:
SOURCE TARGET FSTYPE USED
/dev/vda1 / ext4 3.6G
文件系统的体系结构
一个操作系统中的文件系统有三层结构:
- 物理文件系统
- 虚拟文件系统
- 逻辑文件系统。
不同层次之间既可彼此独立存在,也可紧密耦合为诸多抽象。
当人们谈论文件系统时,他们所指的就是这三层中的某一层。
尽管这些层次在不同操作系统之间有所不同,但这些概念基本是相通的。
物理层是文件系统的具体实现,负责数据存储和检索,以及存储设备上的空间管理(或者更确切地说是分区)。
物理文件系统通过 设备驱动程序与实际的存储硬件进行交互。
下一层是虚拟文件系统,简称 VFS。
虚拟文件系统提供了一种支持在操作系统上安装的各类文件系统的 一致视图。
那么,这是否意味着一个操作系统可以同时使用多种文件系统呢?
答案是肯定的!
可移动存储工具通常都具有与计算机不同的文件系统。
例如,在使用NTFS作为主要文件系统的Windows环境下,闪存可能已格式化为exFAT或FAT32。
也就是说,操作系统需要能够在处理不同程序(文件浏览器和其他处理文件的应用)和不同的挂载文件系统(例如NTFS,APFS,EXT4, FAT32,exFAT和UDF)时提供一种 一致视图。
比如说,当你打开文件资源管理器时,你可以从EXT4文件系统复制一份图像,然后将其直接粘贴到exFAT格式的闪存中,而不必去管文件在后台进行了不同的管理。
VFS堪称连接用户(你)和后台文件系统的“便利层”。
它制定了一种 合同 ,要求所有的物理文件系统都必须以操作系统支持的方式工作。
但是,这种合规性并未内置于文件系统的核心中,也就是说,文件系统的源代码并不包含对各种操作系统的支持。
事实是,这些源代码利用 文件系统驱动程序 来遵守VFS的规则。
驱动程序是一种能够使软件与另一个软件或硬件进行通信的特殊程序。
但用户程序不会直接与VFS交互。
而是利用位于程序和VFS之间的统一API实现交互。
没错,我们接下来要说的就是逻辑文件系统。
逻辑文件系统是文件系统中面向用户的一层。通过提供API,它能使用户程序无需处理任何存储硬件便能执行各种文件操作,例如 打开
, 读
, 写
。
话说回来,VFS也在逻辑文件系统(程序与之交互)和一组物理文件系统之间搭建了桥梁。
文件系统层的高级架构
什么叫挂载文件系统?
在Unix一类的系统上,VFS为每个分区或可移动存储设备都分配一个 device ID (如 dev/disk1s1
)。
接着,它会创建一个 虚拟目录树 ,并将每个设备的内容按照单独的目录放在该目录树下。
而在根目录树下给存储设备分配目录的活动就是 挂载,分配的目录被称为 挂载点。
就是说,在Unix一类的操作系统上,所有分区和可移动存储设备看起来就都好像是根目录下的目录。
例如,在Linux上,除非有所配置,否则默认情况下可移动设备(例如存储卡)的挂载点就是根目录下的 /media
。
也就是说,当将闪存连接到一个操作系统时,它会被 自动挂载 在默认挂载点(在Linux环境下就是 /media
),其内容也将在 /media
目录下显示。
但是,有些时后会需要你手动挂载文件系统。
在Linux上,可进行如下操作:
mount /dev/disk1s1 /media/usb
在上面的命令中,第一个参数(/dev/disk1s1
)是设备ID,第二个参数(/media/usb
)是挂载点。
请注意,挂载点应当已经作为目录存在。
如果没有,则必须先创建它:
mkdir -p /media/usb
mount /dev/disk1s1 /media/usb
文件元数据
文件元数据是一种数据结构,存储 有关文件的数据,比如:
- 文件大小
- 时间戳,如创建日期,上次访问日期和修改日期
- 文件所有者
- 文件权限状态(“谁”可以“如何”处理文件)
- 分区上的哪些块分配给了文件
- 等等
不过,元数据不会与文件内容一起存储,而是存储在磁盘上的其它位置并与文件关联。
在Unix一类的系统中,元数据以一种特殊的数据结构形式存储,被称为 索引节点。
索引节点由唯一的 索引节点号 标识。
索引节点与 索引节点表 (一种数据结构)中的文件关联。
存储设备上的每个文件都有一个索引节点,该索引节点包含有关文件的信息,包括分配给该文件的区块地址。
在一个ext4索引节点中,所分配区块的地址以索引节点中的 区段 (一组数据结构)形式存储。
每个区段包含分配给文件的第一个数据块的地址,以及文件已占用的下面几个区块的数量。
如果文件是分段存储的,每个分段都会有其专属的范围。
这与ext3的指针系统不同,后者是通过间接块指针指向各个数据块的。
使用区段数据结构可使文件系统指向大型文件,但同时又不会占用太多空间。
每当请求文件时,其名称都会首先解析为一个索引节点号。
有了索引节点号之后,文件系统便会从存储设备中获取相应的索引节点。
提取索引节点过后,文件系统便开始根据索引节点中存储的数据块来组成文件。
在Linux上,你可以将 df
命令与 -i
参数一起使用,以查看分区中的索引节点(节点总计,已使用节点和可用节点):
df -i
输出结果如下:
udev 4116100 378 4115722 1% /dev
tmpfs 4118422 528 4117894 1% /run
/dev/vda1 6451200 175101 6276099 3% /
可以看到,分区 /dev/vda1
的索引节点总数为6451200,其中3%(175101个)已被使用。
此外,如果要查看与目录中的文件相关联的索引节点,则可以使用带有 -il
参数的 ls
命令。
ls -li
输出结果如下:
1303834 -rw-r--r-- 1 root www-data 2502 Jul 8 2019 wp-links-opml.php
1303835 -rw-r--r-- 1 root www-data 3306 Jul 8 2019 wp-load.php
1303836 -rw-r--r-- 1 root www-data 39551 Jul 8 2019 wp-login.php
1303837 -rw-r--r-- 1 root www-data 8403 Jul 8 2019 wp-mail.php
1303838 -rw-r--r-- 1 root www-data 18962 Jul 8 2019 wp-settings.php
第一列是与每个文件关联的索引节点号。
分区上的索引节点数是在格式化分区时确定的。也就是说,只要设备上还有可用空间并且有未使用的缩阴节点,文件就可以存储在存储设备上。
事实上,个人版的Linux操作系统不太可能会耗尽索引节点。但是,企业在使用需要处理大量文件的服务(例如邮件服务器)时,就必须聪明地管理其索引节点的配额了。
然而,在NTFS上,元数据的存储方式会有所不同。
NTFS将文件信息保存在 主引导表(MFT) 这一特殊数据结构中。
MFT中,每个文件都至少具有一个条目,和索引节点类似,其中包含有关相应文件的所有内容,包括其在存储设备上的位置。
在大多数操作系统上,你也可以从图形用户界面访问常规文件元数据。
例如,当在Mac OS上右键单击一个文件并选择“获取信息”(即Windows中的“属性”)时,将回弹出一个窗口,其中包含有关该文件的信息。该信息便是从相应文件的元数据中获取的。
空间管理
存储设备内分为大小固定的区块,称为 扇区。
扇区是存储设备上的 最小存储单元,大小介于512字节和4096字节之间(高级格式)。
然而,文件系统实际使用高级概念 区块 作为存储单元。
块是对物理扇区的抽象,由多个扇区组成。
根据文件的大小,文件系统为每个文件分配一个或多个块。
进行空间管理时,文件系统很明确分区上每个 已使用 和 未使用 的块,因此便可以为新文件分配空间或在收到请求时获取现有文件。
ext4格式的分区中最基本的存储单元就是块。
为便于管理,连续的块会被集中在一起,组成 块组。
ext4分区中的块组图示
每个块组都有自己的数据结构和数据块。
H下面是一个块组可以包含的数据结构:
- 超级块: 元数据存储库,其中包含有关整个文件系统的元数据,例如文件系统中的总块数,块组中的块总数,索引节点等。但并非所有的块组都包含超级块。一定数量的块组会存储超级副本作为备份。
- 组描述符: 组描述符同样包含每个块组的簿记信息
- 索引节点位图: 每个块组都有自己的索引节点配额用于存储文件。块位图是一种数据结构,用于标识块组中 已使用 和 未使用 的索引节点。
1
表示已使用的索引节点对象,0
表示未使用的索引节点对象。 - 块位图: 一种数据结构,用于标识块组中已使用和未使用的数据块。
1
表示已使用的数据块,0
表示未使用的数据块。 - 索引节点表: 一种数据结构,用于定义文件及其索引节点的关系。存储在该区域中的索引节点的数量与文件系统使用的块大小有关。
- 数据块: 存储文件内容的块组中的区域。
与ext3相比,Ext4文件系统又往前更进一步,将块组集合成为了一个更大的组,被称为 弹性块组。
每个弹性块组包含多个数字块组。
每个块组的数据结构(包括块位图,索引节点位图和索引节点表)被 串联 在一起并存储在相应的弹性块组内的 首个块组 中。
通过将所有数据结构连接到一个块组(第一个)中,便可以释放每个弹性块组内其它块组上的更多连续数据块。
第一个块组的图示如下:
ext4弹性块组中第一个块的图示
文件被写入磁盘时,会被写入某个块组中的一个或多个块。
通过在块组级别管理文件,可显著提高文件系统的性能。
文件大小vs占用磁盘大小
你是否曾注意到文件浏览器会为每个文件显示两种大小: 文件大小, 和 占用磁盘大小。
文件大小和占用磁盘大小
你可能会问,为什么文件大小和占用磁盘大小会略有不同?
解释如下:
我们已经知道,基于文件大小,一个文件会被分配一个或多个块。
而一个块是可以分配给文件的最小空间,这就意味着那些仅被部分占用的块的剩余空间是不能被另一个文件使用的。
由于文件的大小 并不是块的整数倍,因此很有可能最后一个块仅被部分占用,而该块剩余的空间将保持未使用的状态或会被零填充。
因此,“文件大小”基本上是指文件的实际大小,而“占用磁盘大小”是指文件已占用的空间,即使它没有完全使用这些空间。
在Linux上,你可以使用 du
命令来查看文件大小,
du -b "some-file.txt"
输出结果如下:
623 icon-link.svg
并查看其占用磁盘大小:
du -B 1 "icon-link.svg"
输出结果如下:
4096 icon-link.svg
可以看出,该文件被分配的块约为4kb,而文件的实际大小为623字节。
什么是磁盘碎片?
随着时间的推移,新文件会被写入磁盘,现有文件或增大,或缩小,或被删除。
在存储工具中频繁进行这些更改便会在文件之间留下许多小的间隙(空白空间)。
当文件作为片段存储在存储设备上时便会产生文件碎片,这是因为文件系统已不能找到足够多的连续块来将整个文件存储在一行中。
碎片和非碎片文件的示例
让我们通过一个示例来更好地说明这一点。
假设你有一个名为 myfile.docx
的Word文档。
起初,myfile.docx
存储在磁盘上的几个连续块中,比方说就是 LBA250
, LBA251
和 LBA252
(这种命名方式是假设的)这几个块。
现在,如果你将更多内容添加到了 myfile.docx
中并将其保存,该文档将会在存储工具上占用更多的块。
考虑到 myfile.docx
当前存储在 LBA250
, LBA251
, 和 LBA252
上,因此,根据所需空间,新增加的内容最好存储在紧跟其后的 LBA253
等几个块上。
但是,如果现在 LBA253
已经被另一个文件占用(可能是另一个文件的第一个块)了,那么 myfile.docx
的新增内容就只能存储在磁盘上其它位置的块中,比方说是 LBA312
和 LBA313
。
myfile.docx
got fragmented 💔.
文件碎片会给文件系统带来运行负担,因为每次用户程序请求碎片文件时,文件系统都需要从磁盘上的各个位置收集文件的每个片段。
这一过程也适用于将文件保存回磁盘的情况。
即便是第一次将文件写入磁盘时,也有可能发生碎片,原因可能是因为文件很大,但存储设备上已没有剩余空间。
碎片也是某些操作系统随着文件系统老化而性能变慢的原因之一。
如今我们还有必要担心碎片的问题吗?
一个直截了当的回答是:再也不用了!
利用智能算法,时下的文件系统会尽可能避免(或及早发现)碎片的产生。
Ext4还会进行所谓的 预分配, 就是为文件提前保留一些块以备不时之需,以此来确保即便使用了一段时间文件也不会产生碎片。
预分配块 的数量在文件索引节点对象区段的 长度字段 中有所定义。
此外,Ext4还会使用一种名为 延迟分配 的技术。
该技术不再在写入过程中一次写入一个数据块,而是先将分配请求累积在缓冲区。然后在分配完成后再将数据写入磁盘。
这种方法无需再在回应每个“写”请求时都调用块分配器,从而有助于文件系统在分配可用空间时做出更好的选择,如将大文件与小文件分开放置。
想象如果一个小文件存在了两个大文件之间。那如果删除了这个小文件,则这两个大文件之间便会滞留出一个小空间。
而当大文件和小文件保存在存储设备上的不同区域时,即便删除了小文件,也不会在存储设备上留下很多空白。
通过这种方式散布文件时,还可以在数据块之间留出足够的空隙,进而有助于文件系统更轻松地管理并避免产生碎片。
延迟分配同样可以有效减少碎片并提高系统性能。
目录
目录(Windows中的文件夹)是一种特殊文件,在对文件和目录进行分组时发挥着 逻辑容器 的作用。
目录和文件的处理方式在NTFS和Ext4上是相同的。也就是说,目录只是具有自己的索引节点(在Ext4上)或MFT条目(在NTFS上)的文件。
目录的索引节点或MFT条目包含有关该目录的信息,并指向与该目录相关联文件的条目集合。
实际上,这些文件并不包含在目录中,但它们却以某种方式显示为了目录的子级,从而与目录相关联,就像是一个文件浏览器。
这些条目被称为 目录条目。 目录条目内含有映射到其索引节点或MFT条目的文件名。
除了目录条目,还存在另外两种条目,一个是 .
,指向目录本身,一个是 ..
,指向其上级目录。
在Linux上,可以使用 ls
命令在目录中查看目录条目及其关联的索引节点编号:
ls -lai
输出结果如下:
63756 drwxr-xr-x 14 root root 4096 Dec 1 17:24 .
2 drwxr-xr-x 19 root root 4096 Dec 1 17:06 ..
81132 drwxr-xr-x 2 root root 4096 Feb 18 06:25 backups
81020 drwxr-xr-x 14 root root 4096 Dec 2 07:01 cache
81146 drwxrwxrwt 2 root root 4096 Oct 16 21:43 crash
80913 drwxr-xr-x 46 root root 4096 Dec 1 22:14 lib
...
文件命名规则
有些文件系统会对文件命名实加一些限制。
这种限制可以是 文件名的长度 或是 文件名区分大小写。
例如,在NTFS文件系统中,MyFile
和 myfile
所指的是同一文件,但在EXT4上,它们便指向不同的文件。
你可能会问,这有什么大不了的?
想象一下,假如你正在一台Windows环境的计算机上创建网页。网页内容包含你的品牌徽标,这一徽标为PNG文件,如下所示:
<!DOCTYPE html>
<html>
<head>
<title>Products - Your Website</title>
</head>
<body>
<!--SOME CONTENT-->
<img src="img/logo.png">
<!--SOME MORE CONTENT-->
</body>
</html>
即便该徽标文件的实际命名为 Logo.png
(注意这里是大写的字母 L),你也仍可以在浏览器上打开网页时看到该徽标。
但是在将该网页部署到Linux服务器上进行实时查看时,徽标文件就会受到损坏。
为什么?
因为在Linux(EXT4文件系统)环境下,logo.png
和 Logo.png
分别指向两个不同的文件。
文件大小规则
有关文件系统的一个重要方面是它们支持的 最大文件大小。
就MS-DOS + 7.1、Windows 9x家族和闪存使用的 FAT32 等旧文件系统来说,存储文件大小不能超过4GB,而其后继产品 NTFS 允许的文件大小最高可达 16 EB(1000 TB)。
和NTFS类似,exFAT也支持16EB的文件大小,这也使其成为存储海量数据文件(如视频文件)的理想选择。
实际上,exFAT和NTFS文件系统中对文件大小是没有限制的。
Linux的EXT4和Apple的APFS支持的文件大小分别高达 16 TiB 和 8 EiB。
文件资源管理器
如你所知,文件系统的逻辑层通过提供一个API,可使用户应用程序对文件执行诸如 读
, 写
, 删除
和 执行
等操作。
但文件系统的API其实是一种低级机制,专为计算机程序、运行时环境和壳设计,而非为日常使用而设计。
可以说,操作系统为我们日常的文件管理提供了多种非常方便、开箱即用的文件管理实用程序。例如,Windows上的 File Explorer ,Mac OS上的 Finder ,Ubuntu上的 Nautilus 都是文件浏览器的示例。
这些实用程序在后台使用逻辑文件系统的API。
除了这些GUI(图形用户界面)工具外,操作系统还能通过命令行界面显示文件系统的API,例如Windows上的命令提示符,以及Mac和Linux上的Terminal。
这些基于文本的界面可以帮助用户以文本命令的形式执行各种文件操作,就和我们在前面的示例中所做的一样。
文件访问权限管理
如果说每个人都能删除或修改任何文件,哪怕是非自己所属的文件,或者在没有任何授权的情况下就更新这些文件,后果可想而知。
时下的文件系统故而提供了一种控制用户对文件的访问权限和访问功能的机制。
有关用户权限和文件所有权的数据存储在一个数据结构中。这一数据结构在Windows下被称为访问控制列表(ACL,Access Control List),在Unix一类的操作系统(Linux和Mac OS)上被称为访问控制项(ACE,Access Control Entry)。
此功能在CLI(命令行界面)也同样可用,用户可从命令行界面直接更改文件所有权或设置某个文件的权限。
例如,在Linux或Mac上,文件所有者可以将文件设置为可供所有人使用,步骤如下:
chmod 777 myfile.txt
777
的意思是,每个人都可以对文件 myfile.txt
进行读、写、执行等任何操作。
维护数据完整性
假设你有一项论文研究已经进行了一个月。有一天,你打开论文文件,进行了一些修改,然后将其保存。
而在你点击保存文件后,你的文字处理器程序便会向文件系统的API(逻辑文件系统)发送“写入”请求。
该请求最终会传递到物理层,以将文件修改存储在诸多块中。
但是,如果在将文件的旧版本替换为新版本的过程中系统突然崩溃了该怎么办呢?
哈哈,在较老的文件系统(如FAT32或ext2)中,那意味着你的数据将面临损坏,因为只有部分数据被写入了磁盘。
但在今天,journaling 技术的使用让这种情况已不太可能再发生在时下的文件系统上。
日志文件系统记录着物理层中将要发生但尚未发生的每一项操作。
记录的主要目的就是跟踪记录物理层上尚未提交至文件系统的更改。
日志是磁盘上的一种特殊空间分配。在这里,每一次的“写入”尝试都将首先作为 事务 进行存储。
当数据存储在存储设备的物理层后,这一更改会立即被提交至文件系统。
发生系统崩溃时,文件系统将检测未完成的事务并将其回滚,就好像该事务从未发生一样。
这样一来,新内容(正在写入的内容)可能仍会丢失,但现有数据将完好无损。
诸如NTFS、APFS和EXT4(甚至EXT3)一类的现代文件系统都已使用日志功能来避免系统故障造成的数据损坏。
数据库文件系统
大多文件系统通常都会将文件组织为目录形式。
要访问某个文件,只需层层“跨越”相关目录直至文件所在目录。
cd /music/country/highwayman
但是,在数据库文件系统中,并不存在路径和目录的概念。
数据库文件系统是一个根据多种 属性 和 维度 对文件进行归组的 多面系统。
比如,它可以把一堆MP3文件按照艺术家、流派、发行年份和专辑等多种属性同时陈列。
数据库文件系统更像是一种高级应用程序,它可以帮助你更轻松、更有效地组织和访问文件。但是,你将无法访问此应用程序之外的文件。
然而,数据库文件系统并不能取代传统的文件系统。它只是一个可以高效处理文件的高级抽象。
Mac OS上的应用程序 iTunes 就是数据库文件系统的一个范例。
小结
哇!你读到了最后,这也意味着现在的你已经懂得了很多文件系统的知识。但我也相信,这绝不是你文件系统学习的终点。
再次回到那个问题,我们能用一句话描述一下什么是文件系统以及它是如何工作的吗?
让我们就以我在开头写下的那句简单定义来结束本文吧:
文件系统 决定着在存储设备中对文件进行 命名、 存储 和 检索 的方式。
如果你觉得我的讲解有遗漏之处或是我搞错了一些问题,欢迎在下面评论给我留言哦。
顺便告诉你,如果你想获得更多相关内容的详细讲解,欢迎访问我的网站 skillupp.tech 并关注我的 Twitter,我经常在这两个地方分享我的日常发现。
再次感谢阅读,祝你学得开心哦!
原文:What Is a File System? Types of Computer File Systems and How they Work – Explained with Examples,作者:Reza Lavarian