Bonky Zhu
If someone is able to show me that what I think or do is not right, I will happily change, for I seek the truth, by which no one was ever truly harmed. It is the person who continues in his self-deception and ignorance who is harmed.

Linux 磁盘与文件系统管理 – 文件系统

文件系统

格式化:这是因为每种操作系统所设定的文件属性/权限并不相同, 为了存放这些文件所需的数据,因此就需要将分区槽进行格式化,以成为操作系统能够利用的文件系统格式。

通常来说,一个分区槽就是只能够被格式化成为一个文件系统。但是由于新技术的利用,比如 LVM 与 Software RAID, 这些技术可以将一个分区槽格式化为多个文件系统 (例如 LVM ),也能够将多个分区槽合成一个文件系统 ( LVM,RAID )。

superblock,inode 和 block

文件系统通常会将权限与属性放置到 inode 中,实际数据则放置到 data block 区块中。 另外,还有一个超级区块 (superblock) 会记录整个文件系统的整体信息,包括 inode 与 block 的总量、使用量、剩余量等。

image-20191228081622065

每个 inode 与 block 都有编号,至于这三个数据的意义可以简略说明如下:

  • superblock:记录此 filesystem 的整体信息,包括 inode/block 的总量、使用量、剩余量, 以及文件系统的格式与相关信息等;
  • inode:记录文件的属性,一个文件占用一个 inode,同时记录此文件的数据所在的 block 号码;
  • block:实际记录文件的内容,若文件太大时,会占用多个 block 。

找到文件的 inode 的话,那么自然就会知道这个文件所放置数据的 block 号码, 当然也就能够读出该文件的实际数据了。注意文件系统一开始就将 inode 与 block 规划好了,除非重新格式化(或者利用resize2fs 等指令变更文件系统大小),否则 inode 与 block 固定后就不再变动。

索引文件系统

每个 inode 里面包括了所有的 block,根据存放顺序可以一次读出所有 block。Ext2/Ext3 都是索引式文件系统。一般来说索引文件系统不需要磁盘碎片整理。

image-20191229004334699

FAT 文件系统

以一个链表进行读写,因为写入的 block 过于离散,所以需要碎片整理。

image-20191229005422404

Extent(区间) 文件系统

NTFS/Ext4 使用的是 Extent 文件系统,相当于索引式文件系统的一个改进。如一个文件占用了10个 block,在索引式文件系统中,inode 会分别记录这10个block的位置,而在 extent 文件系统中,如果这10个 block 是地址连续的,那么只会记录第一个 block 的位置以及 extent 的 block 数。这样索引空间占用率较少,连续读写效率比较高。

Ext2 文件系统

image-20200109173536393

ext2 文件系统如图所示,文件系统被分为多个 Block Group(主要是如果把所有的 Block 和 Inode 全放一起的话,不容易管理)。文件系统最前面有一个启动扇区 (boot sector),这个启动扇区可以安装开机管理程序,我们就能够将不同的开机管理程序安装到个别的文件系统最前端,而不用覆盖磁盘唯一的 MBR, 制作出多重引导的环境。

ps:Linux 通过 df -T 可以查看文件系统格式。

数据块 Data Block

Ext2 文件系统中所支持的 block 大小有 1K, 2K 及 4K 三种。

image-20200109174716796

Inode 表

记录文件的属性以及该文件实际数据是放置在哪些 block,一般记录的数据有下面:

  • 存取模式
  • 拥有者与群组
  • 文件大小
  • 建立或状态改变的时间 ctime,最近一次的读取时间 atime,最近修改的时间 mtime
  • 定义文件特性的标志(flag),如 SetUID...
  • 该文件真正内容的指针(pointer)

inode 的数量与大小在格式化时固定了,inode 有以下特点:

  • 每个 inode 大小均固定为 128 bytes (新的 ext4 与 xfs 可设定到 256 bytes)
  • 每个文件都仅会占用一个 inode
  • 文件系统能够建立的文件数量与 inode 的数量有关

但是,有一个问题,每个 block 号码需要 4Byte 的空间,这么算来总共也才32个 block,这样是远远不够的,所以往往使用的是混合索引方法,inode 记录 block 号码的区域定义为 12 个直接索引,一级二级三级间接索引各一个。

image-20200109180817295

一个 inode 最大的文件大小计算方法如下:首先是12个直接指向,12*1K=1K。然后间址和 block 大小有关,如 1K 大小的可以指向 256 个 block。所以一级间址能记录 256*1K=256K,二级间址能记录 64M,三级间址能记录 16G。

注意:当block单位容量为4K时,由于文件系统本身的限制 (因为 MBR 最大支持一个分区为2T,而 GPT 可达 16EB),所以才与计算的结果不太吻合。

超级区块 Superblock

Superblock 是记录整个 filesystem 相关信息的地方,没有 Superblock ,就没有这个 filesystem 了。主要记录的信息有:

  • block 与 inode 的总量
  • 未使用与已使用的 inode / block 数量
  • 一个 block 与 inode 的大小
  • 文件系统相关信息(挂载时间)
  • valid bit,标记是否被挂载

dumpe2fs 可以查看 superblock 信息:

屏幕快照 2020-01-09 下午6.40.25

除了第一个 block group 内会含有 superblock 之外,后续的 block group 不一定含有 superblock。如果有的话,那么主要是为了作为备份,方便于维护。

文件系统描述说明

这个区段可以描述每个 block group 的开始与结束的 block 号码,以及说明每个区段分别介于哪一个 block 号码之间等等。这部份也能够用 dumpe2fs 来观察的。

Group 0: (Blocks 0-32767)
  主 superblock at 0, Group descriptors at 1-4
  保留的GDT块位于 5-512
  Block bitmap at 513 (+513), Inode bitmap at 514 (+514)
  Inode表位于 515-1026 (+515)
  31733 free blocks, 8179 free inodes, 2 directories
  可用块数: 1035-32767
  可用inode数: 14-8192


根据上面的,我们可以看出主 superblock 位于0,Block bitmap 和 Inode bitmap 位于513 和 514,Inode 表位于 515-1026。

区块位示图 block bitmap & inode 位示图

从 block & inode bitmap 当中可以知道哪些 block & inode 是空的。位示图是利用二进制的一位来表示磁盘中一个盘块的使用情况,磁盘上所有的盘块都有一个二进制位与之对应。当其值为 0 时,表示对应的盘块空闲,否则代表已分配。

image-20200109190045357

dumpe2fs 查询 Ext 家族的 superblock

$ dumpe2fs [-bh] 设备文件名
选项与参数:
-b :列出保留为坏轨的部分
-h :仅列出 superblock 的数据,不会列出其他的区段内容!


文件系统目录

当我们在 Linux 下的文件系统建立一个目录时,文件系统会分配一个 inode 与至少一块 block 给该目录。其中,inode 记录该目录的相关权限与属性,并可记录分配到的那块 block 号码; 而 block 则是记录在这个目录下的文件名与该文件名占用的 inode 号码数据。

利用 ls -i 我们可以查看每个文件或者目录所对应的 inode 号码。

屏幕快照 2020-01-10 上午10.11.06

目录树(文件)的读取

以读取 /etc/passwd 这个文件为例。

$ ll -di / /etc /etc/passwd
128 dr-xr-xr-x. 17 root root 4096 May 4 17:56 /
33595521 drwxr-xr-x. 131 root root 8192 Jun 17 00:20 /etc
36628004 -rw-r--r--. 1 root root 2092 Jun 17 00:20 /etc/passwd

  • 透过挂载点的信息读取 / 的 inode,号码是128,然后因为有 r 与 x 的权限,可以读取该 block
  • 的内容。
  • 然后找到 / 所对应的 block,然后在 block 里面找到 /etc 的 inode 号码为33595521
  • 读取 33595521 号 inode,因为 r 与 x 的权限,因此可以读取 /etc 的 block 内容。
  • 经过上个步骤取得 block 号码,并找到该内容有 passwd 文件的 inode 号码为 36628004
  • 读取 36628004 号 inode,由于具有 r 的权限,因此可以读取 passwd 的 block 内容

文件的写入

新建一个文件或目录时候,文件系统会按照以下步骤处理:

  1. 先确定用户对于欲新增文件的目录是否具有 w 与 x 的权限,若有的话才能新增
  2. 根据 inode bitmap 找到没有使用的 inode 号码,并将新文件的权限/属性写入
  3. 根据 block bitmap 找到没有使用中的 block 号码,并将实际的数据写入 block 中,且更新 inode 的 block 指向数据
  4. 将刚刚写入的 inode 与 block 数据同步更新 inode bitmap 与 block bitmap,并更新 superblock 的内容

一般来说,我们将 inode table 与 data block 称为数据存放区域,至于其他例如superblock、 block bitmap 与 inode bitmap 等区段就被称为 metadata (元数据) 。

日志式文件系统

在日程使用过程中,由于各种不可控因素导致系统中断,所以写入的数据仅有 inode table 及 data block 而已, 最后一个同步更新 metadata 的步骤并没有做完,此时就会发生 metadata 的内容与实际数据存放区产生不一致的情况了。操作系统会通过 valid bit (是否有挂载) 与 filesystem state (clean 与否) 等状态来判断是否强制进行数据一致性的检查。

为了避免上述提到的文件系统不一致的情况发生,于是我们在 filesystem 当中规划出一个区块,该区块专门在记录写入或修订文件时的步骤, 因此可以简化一致性检查的步骤了。

  • 预备:当系统要写入一个文件时,会先在日志记录区块中纪录某个文件准备要写入的信息
  • 实际写入:开始写入文件的权限与数据;开始更新 metadata 的数据。
  • 结束:完成数据与 metadata 的更新后,在日志记录区块当中完成该文件的纪录。

查看 dumpe2fs 可以看到以下内容

Journal inode:            8
Journal backup:           inode blocks
Journal features:         journal_incompat_revoke
日志大小:             128M
Journal length:           32768
Journal sequence:         0x000029f3
Journal start:            27846


注意,Ext2 相较于 Ext3 只不过是少了个日志系统,所以利用 tune2fs -j /dev/sda1 可以转换到 Ext3。

查看日志的方法可以参考: What kind of data is stored in the ext4 file system's journal?

以及通过日志恢复文件的方法:Linux文件恢复利器 ext3grep 与 extundelete

文件系统的运作

为了解决磁盘写入和内存的速度差问题,Linux 使用的方式是异步处理:

  • 当加载一个文件到内存后,如果该文件更动,则在内存中的文件数据会被设定为干净的。 但如果内存中的文件数据被更改过了,此时该内存中的数据会被设定为脏的。此时所有的动作都还在内存中执行,并没有写入到磁盘中。
  • 系统会不定时的将内存中设定为脏的数据写回磁盘,以保持磁盘与内存数据的一致性。 当然也可以使用 sync 指令来手动写入磁盘。

系统会将常用的文件数据放置到主存储器的缓冲区,以加速文件系统的读/写。因此,因此 Linux 的物理内存最后都会被用光,这是正常的情况,可加速系统效能。

但若不磁盘正常断开导致数据尚未回写到磁盘内, 重新插入后可能会花很多时间在进行磁盘检验,甚至可能导致文件系统的损毁(非磁盘损毁)。macOS 未弹出进行插拔后需要很长时间才能读取也是这种情况(Window 则可以热插拔)

挂载点

挂载点是一个目录,该目录为进入该文件系统的入口。一般来说利用 df 命令就可以看到挂载点。

其它文件系统

可以通过以下命令可以看到 Linux 所支持的文件系统:

ls /lib/modules/$(uname -r)/kernel/fs


可以看到一下结果:

image-20200110113809242

下面的每个文件夹里面包含了文件系统的 .ko 文件(驱动模块文件),驱动模块的编写请参考:Linux下编写和加载 .ko 文件(驱动模块文件)

然后查看系统目前已加载到内存中支持的文件系统:

cat /proc/filesystems


VFS

VFS (Virtual Filesystem Switch) 抽象了一层接口,然后用户利用 VFS 来管理下面各种类型的文件系统,就无需知道下面文件系统的类型。

image-20200110114538410

XFS

我们都知道格式化的时候,Ext 文件系统会预先规划出所有的 inode/block/meta data 等等数据,后期就不会再改变了。但是现在磁盘越来越大(都是 PB 或者 EB 容量级),预先分配 inode 与 block 实在太浪费时间了(往往很大的磁盘甚至需要一天的时间)。

由于虚拟化的应用越来越广泛,而作为虚拟化磁盘来源的巨型文件 (单一文件好几个 GB 以上!) 也就越来越常见了。 这种巨型文件在处理上需要考虑到效能问题,否则虚拟磁盘的效率就会不太好看。因此,从 CentOS 7.x 开始, 文件系统已经由预设的 Ext4 变成了 xfs 这一个较适合高容量磁盘与巨型文件效能较佳的文件系统。

xfs 文件系统在资料的分布上,主要规划为三个部份,一个数据区 (data section)、一个日志区 (log section) 以及一个实时区 (realtime section)。

数据区

与 ext 家族的 block group 类似,也是分为多个储存区群组 (allocation groups) 来分别放置文件系统所需要的数据。每个储存区群组都包含了:

  • 整个文件系统的 superblock
  • 剩余空间的管理机制
  • inode 的分配与追踪

此外,inode 与 block 都是系统需要用到时, 这才动态配置产生,所以格式化动作很快。

另外,与 ext 家族不同的是, xfs 的 block 与 inode 有多种不同的容量可供设定,block 容量可由 512bytes ~ 64K 调配,不过,Linux 的环境下, 由于内存控制的关系 (页面文件 pagesize 的容量之故),因此最高可以使用的 block 大小为 4K 。(格式化 block 成为 16K 是没问题的,不过,Linux 核心不给挂载) 至于 inode 容量可由 256bytes 到 2M 这么大!不过,大概还是保留 256bytes 的默认值就很够用了。

日志区

主要被用来纪录文件系统的变化。你也可以指定外部的磁盘来作为 xfs 文件系统的日志区。

实时区

当有文件要被建立时,xfs 会在这个区段里面找一个到数个的 extent 区块,将文件放置在这个区块内,等到分配完毕后,再写入到 data section 的 inode 与 block 去。一般来说默认为 64K,具有类似磁盘阵列的 stripe 情况下,则建议 extent 设定为与 stripe 一样大较佳。

xfs_info 检查 superblock

检查 xfs 的分区如下:

$ xfs_info /dev/sdb2
meta-data=/dev/sdb2              isize=512    agcount=4, agsize=122094272 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=0 spinodes=0
data     =                       bsize=4096   blocks=488377088, imaxpct=5
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
log      =internal               bsize=4096   blocks=238465, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

  • 第 1 行里面的 isize 指的是 inode 的容量,每个有 512bytes 这么大。至于 agcount 则是前面谈到的储存区群组 (allocation group) 的个数,共有 4 个, agsize 则是指每个储存区群组具有 122094272 个 block 。第 4 行的 block 设定为 4K,因此整个文件系统的容量应该就是 4*122,094,272*4K=1,953,508,352=2T 这么大!(实质上也的确如此)
  • 第 2 行里面 sectsz 指的是逻辑扇区 (sector) 的容量设定为 512bytes
  • 第 4 行里面的 bsize 指的是 block 的容量,每个 block 为 4K 的意思,共有 488,377,088 个 block 在这个文件系统内
  • 第 5 行里面的 sunit 与 swidth 与磁盘阵列的 stripe 相关性较高。
  • 第 7 行里面的 internal 指的是这个登录区的位置在文件系统内,而不是外部设备的意思。且占用了 4K*238465 个 block,总共约 900M 的容量
  • 第 9 行里面的 realtime 区域,里面的 extent 容量为 4K。不过目前没有使用。

macOS 的 APFS 文件系统

克隆和数据完整性

img

APFS 使用称为写时复制(copy-on-write)的方案来生成重复文件的即时克隆。在 HFS+ 下,当用户复制文件的时候,每一个比特(二进制中的“位”)都会被复制。而 APFS 则通过操作元数据并分配磁盘空间来创建克隆。但是,在修改复制的文件之前都不会复制任何比特。当克隆体与原始副本分离的时候,那些改动(并且只有那些改动)才会被保存。

写时复制还提高了数据的完整性。在其它系统下,如果你卸载卷导致覆写操作挂起的话,你可能会发现你的文件系统有一部分与其它部分不同步。写时复制则通过将改动写入到可用的磁盘空间而不是覆盖旧文件来避免这个问题。直到写入操作成功完成前,旧文件都是正式版本。只有当新文件被成功复制时,旧文件才会被清除。

系统快照

img

快照是写时复制架构给你带来的一个主要的升级。快照是文件系统在某个时间点的一个只读的可装载映像。随着文件系统发生改动,只有改动的比特会被更改。这可以让备份更简单,更可靠。考虑到时间机器(一个苹果出品的备份工具)已经成为硬链接的痛点,这可能是一个重大的升级。

输入/输出的服务质量(QoS)

QoS 优先分配带宽使用以避免降低优先任务的速度。苹果的 QoS 会优先考虑用户操作,例如活跃窗口。而诸如时间机器备份这些后台任务将会被降级。

本地加密

img

在后斯诺登时代,加密成为众所关注的了。越来越多的苹果产品正在强调其系统安全性。内置强大的加密机制并不让人感到意外。包括 APFS 在内,苹果正在采用更加细致入微的加密方案,要么不加密,要么就将加密进行到底。用户可以使用单个密钥来为所有数据加密,或者使用多个加密密钥分别锁定单个文件和文件夹。当然,你也可以不加密,只要你对坏蛋无所忌惮。

固态硬盘和闪存优化

img

闪存优化已经被列为 APFS 的一个亮点功能,不过它的实现并没有那么振奋人心。苹果选择将一些典型的固态硬盘芯片的处理功能迁移到操作系统,而没有深度系统集成的优势。这更像是让文件系统感知固态硬盘,而不是为它们做优化。

动态分区调整

img

APFS 驱动器的逻辑分区可以动态调整自身大小。用户只需指定所需分区的数量,然后文件系统会在运行时进行磁盘分配。每个分区只占用其用于存储文件的磁盘空间。剩余的磁盘空间会由任何分区获取。这种设计很整洁,不过比起其它文件系统,这更像是元文件夹。

Share

You may also like...

发表评论