Linux 主流文件系统

查看文件系统支持

Linux 系统现支持的文件系统格式如下:

文件系统 描述
ext Linux 扩展文件系统(Extended Filesystem),最早的 Linux 文件系统。
ext2 第 2 代扩展文件系统,在 ext 的基础上提供了更多的功能。ext2 支持 undelete(反删除)和大文件。
ext3 第 3 代扩展文件系统,支持日志功能。ext3 支持大文件。但不支持反删除(undelete)操作。
ext4 第 4 代扩展文件系统,支持高级日志功能。
hpfs OS/2 高性能文件系统
jfs IBM 日志文件系统。
iso9660 ISO 9660 文件系统(CD-ROM)。
minix MINIX 文件系统。
msdos 微软的 FAT16。
ncp Netware 文件系统。
nfs 网络文件系统。
ntfs 支持 Microsoft NT 文件系统。
proc 访问系统信息。
ReiserFS 高级 Linux 文件系统,支持大文件,支持反删除,几乎能恢复 90% 以上的数据,有时能恢复到 100%。
smb 支持网络访问的 Samba SMB 文件系统。
sysv 较早期的 Unix 文件系统。
ufs BSD 文件系统。
umsdos 建立在 msdos 上的类 Unix 文件系统。
vfat Windows 95 文件系统(FAT32)。
XFS 高性能 64 位日志文件系统。

可以通过查看 fs 目录了解支持的文件系统有哪些:

[root@101c7 /]$ ls -l /lib/modules/$(uname -r)/kernel/fs
total 20
-rw-r--r--. 1 root root 5992 Aug 31 11:05 binfmt_misc.ko.xz
drwxr-xr-x. 2 root root   25 Sep  7 05:54 btrfs
drwxr-xr-x. 2 root root   30 Sep  7 05:54 cachefiles
drwxr-xr-x. 2 root root   24 Sep  7 05:54 ceph

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

[root@101c7 /]$ cat /proc/filesystems 
nodev   sysfs
nodev   rootfs
nodev   ramfs

整个 Linux 系统都是通过虚拟文件系统(VFS,Virtual Filesystem Switch)的内核功能来读取文件系统。虚拟文件系统会管理各种文件系统中的数据,并统一提供给系统调用使用。

日志式文件系统

Linux 中有三种广泛使用的日志文件方法,每种的保护等级都不相同:

方法 描述
数据模式 索引节点和文件都会被写入日志。所有写入储存设备的数据都要写两次,因此数据安全但性能较差。
有序模式 只有索引节点数据会被写入日志,数据写入成功后立即删除。
回写模式 只有索引节点数据会被写入日志,但不控制文件数据何时写入。

异步处理

当系统加载一个文件到内存后,如果长时间没有改动,则内存区段中的文件数据会被设置为 clean。如果更改了,则设置为 dirty。系统会不定时将内存中设置为 dirty 的数据写回磁盘。这个过程叫做异步处理(Asynchronously)。

  • 系统会将常用的文件数据放置在主储存器的缓冲区,以加速文件系统的读写。
  • 可以通过 sync 命令将 dirty 数据写回到硬盘。
  • 正常关机时会调用 sync 将内存中数据写回到硬盘。
  • 非正常关机时可能会造成数据损毁。日志文件系统下次启动时会读取日志文件并处理上次留下的未写入的数据。

数据不一致状态

假如文件写入过程中突然断电,写入的数据仅有 inode table 和 data block,最后一个同步更新中间数据的步骤没有完成就会发生数据不一致状态(Inconsistent)。

在 Ext2 文件系统中发生这个问题,系统在重启后会通过 Super Block 当中记录的 valid bit(是否有挂载)与文件系统的 state 等状态判断是否强制进行数据一致性的检查,需要的话运行磁盘扫描程序。

日志文件系统种类

日志文件系统(Journaling file system)为了避免文件系统不一致的情况发生,在文件系统中规划出一个块,用来记录写入或修改文件时的步骤。

  • 当系统要写入一个文件时,会先在日志记录块中记录该文件准备要写入的信息。
  • 当完成数据与元数据的更新后,将该文件的记录标记为完成。
  • 这样当出现问题时,系统只需检查日志记录块,就知道哪个文件发生了问题,从而进行一致性检查。

在上述示例中的 /dev/sdb1 中,journal 块指向 inode 8 号记录,其容量为 16MB,用于处理日志记录。

常见的日志文件系统类型如下:

  • ext3 文件系统

  • 采用与 ext2 文件系统相同的索引节点表结构,但为每个存储设备增加了一个日志文件,将准备写入存储设备的数据先记录在日志中。

    • 默认情况下,ext3 文件系统使用有序模式的日志功能。也可以在创建文件系统时添加选项,将日志方法更改为数据模式或回写模式。
  • ext4 文件系统

    • 除了支持数据压缩和加密,ext4 文件系统还引入了一种称为"区段"(Extent)的特性。区段在存储设备上按块分配空间,但在索引节点表中只保留起始块的位置。
    • 另一个特性是块预分配技术(Block Preallocation)。如果要为一个会变大的文件预留空间,ext4 文件系统可以为该文件预分配所有需要的块,并使用 0 填充。
  • Reiser 文件系统

    • ReiserFS 文件系统仅支持回写日志模式,并具有两个特性。
    • 第一个特性是在线调整已有文件系统的大小。
    • 第二个特性是尾部压缩处理(Tailpacking),可以将一个文件的数据填入另一个文件的数据块中的空白空间,从而节省容量。
  • JFS 文件系统

    • JFS 文件系统采用有序日志方法。其特点是采用基于区段的文件分配,为每个写入存储设备的文件分配一组块,以减少存储设备上的碎片。
  • XFS 文件系统

    • XFS 文件系统采用回写日志模式,并与 Reiser 类似,支持在线调整文件系统大小。
    • 需要注意的是,XFS 文件系统只能扩容,无法缩容。

Ext 文件系统结构

在 Linux 中,选择分区格式化的目的是选择文件系统,并按照文件系统标准对数据进行初始化。

当选择 ext 文件系统进行格式化后,磁盘结构将包含一个引导扇区(用于安装操作系统)和多个块组(Block Group)。具体结构如下图所示:

Ext文件系统结构

[启动扇区(含0号block)][SuperBlock][File System Description][块对照表][Inode对照表][Inode表格][数据块]

其中,启动扇区占用的大小为 1KB。当块的大小设为 1024B 时,启动扇区将占用 0 号 block。而在块的大小超过 1024B 时,启动扇区之前是引导扇区,其后是占用 1K 的超级块(SuperBlock)。

每个块组又包含分为六个区域:

  • 超级块(SuperBlock):记录文件系统的整体信息,包括 inode 和 block 的总量、使用量、剩余量,以及文件系统的格式和相关信息。
  • 文件系统描述(File System Description):描述每个块组的起止 block 号码,并说明块组中每个区域(superblock、inode、data、bitmap)的起止 block 号码。
  • 块对照表(Block Bitmap):辅助块组找到可用的 block 来写入数据。在删除文件时,相应的 block 号码会被标记为未使用。
  • Inode 对照表(Inode Bitmap):类似于块对照表,用于记录已使用和未使用的 inode 号码。
  • Inode 表格(Inode Table):记录文件的属性,每个文件占用一个 inode,并记录该文件数据所在的 block 号码。
  • 数据块(Data Block):实际存储文件内容的区域。如果文件大小超过一个 block 的容量,将占用多个数据块。

与 FAT 文件系统对比:

  • Linux 文件系统中,每个 inode 和 block 都有编号。在读取文件时,先读取相应的 inode 记录,得到文件实际存放的 block 号码,然后将整个文件读取出来。这种数据访问方法被称为索引式文件系统(indexed allocation)。
  • FAT 文件系统中没有 inode 的概念,每个 block 号码都记录在前一个 block 中。在读取文件时,必须逐个追踪这些 block,硬盘需要转动多次才能读取到最后一个 block。因此,需要进行磁盘碎片整理,将同一个文件所占用的 block 整理在一起,以提高磁头搜索效率。

数据块

数据块是放置文件内容的地方,在 Ext2 中支持的块大小有 1KB、2KB 和 4KB,格式化时可选择块大小,每个块都有编号。

不同块大小支持的分区大小和单文件大小有所不同:

块大小 1KB 2KB 4KB
最大单一文件限制 16GB 256GB 2TB
最大文件系统总容量 2TB 8TB 16TB

每个 block 内最多放置一个文件的数据,如果文件超过 block 大小,会占用多个 block。文件大小小于 block 大小时,block 内剩余容量也不能再被利用。

Inode 表格

inode 的内容主要记录文件的属性及文件实际数据存放的 block 号码。

inode 记录的文件数据有下面这些:

  • 该文件的访问模式(read/write/execute);
  • 该文件的所有者与组(owner/group);
  • 该文件的大小;
  • 该文件创建或状态改变的时间(ctime);
  • 最近一次读取的时间(atime);
  • 最近修改的时间(mtime);
  • 定义文件特性的标志(flag),比如 SUID 等;
  • 该文件真正内容的指向(pointer)。

inode 的特点:

  • 每个 inode 大小均固定为 128Bytes(ext4 和 xfs 可设置到 256Bytes);
  • 每个文件都仅占用一个 inode;
  • 文件系统能创建的文件数量与 inode 数量有关;
  • 系统读取 inode 时,会匹配 inode 上记录权限与用户是否符合,符合才继续读取 block 内容。

inode 记录方式:

  • 每个 inode 记录 block 号码的区域定义为:12 个直接,1 个间接,1 个双间接和 1 个三间接记录区;
  • 12 个直接记录能直接取得 block 号码,间接则是再拿一个 block 来当作记录 block 号码的记录区。双间接和三间接则是 block 再次指向下一个记录编号的 block 号码,以此最多三层指向;
  • 以 1k 大小 block 块计算,总共能记录的文件大小为:(12×1K)+(1K/4B=256K)+(256×256K)+(256×256×256K)=16GB(12\times1K)+(1K/4B=256K)+(256\times256K)+(256\times256\times256K)=16GB
  • 现在使用 256Bytes 容量大小的 inode,还可以记录更多文件系统信息,包括 ACL 及 SELinux 类型等。记录的单一文件大小达 16TB,且单一文件系统总容量可达 1EB。

超级块

超级块是记录整个文件系统相关信息的地方,一般超级块的大小为 1024 字节。

超级块记录的信息有:

  • block 与 inode 的总量;
  • 未使用与已使用的 inode/block 数量;
  • block 与 inode 的大小;
  • 文件系统的挂载时间、最近一次写入数据的时间、最近一次检验磁盘(fsck)的时间等;
  • 一个 valid bit 数值,若此文件系统已被挂载则值为 0,未挂载为 1。

由于超级块的重要性,除了第一个块组中含有超级块外,其他块组中可能还存在超级块的备份,用于在关键时刻进行救援修复。

查询文件系统详情

使用 dumpe2fs 命令可以查询 ext 文件系统信息。例如查询设备 /dev/sdb1

[root@101c7 /]$ dumpe2fs /dev/sdb1
dumpe2fs 1.42.9 (28-Dec-2013)
Filesystem volume name:   <none>
Last mounted on:          <not available>
Filesystem UUID:          3789ee36-8f38-441c-ac01-c0a9ca8171a1
Filesystem magic number:  0xEF53
Filesystem revision #:    1 (dynamic)
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype needs_recovery sparse_super large_file
Filesystem flags:         signed_directory_hash 
Default mount options:    user_xattr acl
Filesystem state:         clean
Errors behavior:          Continue
Filesystem OS type:       Linux
Inode count:              65536
Block count:              261888
Reserved block count:     13094
Free blocks:              253344
Free inodes:              65525
First block:              0
Block size:               4096
Fragment size:            4096
Reserved GDT blocks:      63
Blocks per group:         32768
Fragments per group:      32768
Inodes per group:         8192
Inode blocks per group:   512
Filesystem created:       Thu Sep  9 15:10:34 2021
Last mount time:          Thu Sep  9 15:11:11 2021
Last write time:          Thu Sep  9 15:11:11 2021
Mount count:              1
Maximum mount count:      -1
Last checked:             Thu Sep  9 15:10:34 2021
Check interval:           0 (<none>)
Reserved blocks uid:      0 (user root)
Reserved blocks gid:      0 (group root)
First inode:              11
Inode size:               256
Required extra isize:     28
Desired extra isize:      28
Journal inode:            8
Default directory hash:   half_md4
Directory Hash Seed:      32744b35-d887-4ba9-9431-c5a9d63b9474
Journal backup:           inode blocks
Journal features:         (none)
Journal size:             16M
Journal length:           4096
Journal sequence:         0x00000002
Journal start:            1


Group 0: (Blocks 0-32767)
  Primary superblock at 0, Group descriptors at 1-1
  Reserved GDT blocks at 2-64
  Block bitmap at 65 (+65), Inode bitmap at 66 (+66)
  Inode table at 67-578 (+67)
  32183 free blocks, 8181 free inodes, 2 directories
  Free blocks: 585-32767
  Free inodes: 12-8192
Group 1: (Blocks 32768-65535)
  Backup superblock at 32768, Group descriptors at 32769-32769
  Reserved GDT blocks at 32770-32832
  Block bitmap at 32833 (+65), Inode bitmap at 32834 (+66)
  Inode table at 32835-33346 (+67)
  32189 free blocks, 8192 free inodes, 0 directories
  Free blocks: 33347-65535
  Free inodes: 8193-16384
...

可以看到这块磁盘格式化成了 ext3 文件系统后的 inode/block 数量,目前状态为 clean 正常等,这些信息是由 superblock 记录的。后面以 Group 开头的段为各个块组的信息。

XFS 文件系统结构

由于 Ext 文件系统在格式化时采用的是预先规划出所有 inode/block/metadata 等数据,当磁盘容量很小时问题不大,但处理按 TB 计的大硬盘时,仅格式化就需要花费很长时间。

而 XFS 是针对大文件和数据设计的日志式文件系统。XFS 具备了 Ext 文件系统的所有功能,因此被作为 CentOS 7 中的默认文件系统。

XFS 文件系统在数据分布上主要划分为三部分:数据区、文件系统活动登录区和实时运行区。

数据区

数据区(Data Section)结构基本与 Ext 文件系统相同,包括 inode/data block/superblock 等数据,并且也是分为多个存储区组(Allocation Groups)来分别放置文件系统所需数据。

每个存储区组都包含了:

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

此外,inode 与 block 都是系统需要用时才动态配置产生。block 容量可以设置成 512 字节到 64KB,inode 的容量可以设置成 256 字节到 2MB。这是与 Ext 文件系统的主要区别。

文件系统活动日志区

在文件系统活动日志区(Log Section)中,主要被用来记录文件系统的变化,有点像日志系统。文件的变化会在这里记录下来,直到变化完整写入到硬盘后,记录才会终止。

在遇到突发情况而造成文件系统损毁时,系统会检查日志区,看系统之前正在操作的文件,再检查文件是否正确,依次修复文件系统。

由于此区块读写频繁,因此可以指定单独的磁盘作为 XFS 文件系统的日志区。

实时运行区

当有文件要被创建时,XFS 会在实时运行区(Realtime Section)中找一到多个 extent 区块,将文件放置在这个区块内,等到分配完成再写入到数据区的 inode 和 block 中。

extent 区块在格式化时可以指定,范围从 4KB 到 1GB,一般默认设置为 64KB 容量。

查询文件系统详情

在 XFS 文件系统中,使用 xfs_info 命令来查看分区详情:

[root@101c7 ext333]$ xfs_info /dev/mapper/centos-root
meta-data=/dev/mapper/centos-root isize=512    agcount=4, agsize=1113856 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=0 spinodes=0
data     =                       bsize=4096   blocks=4455424, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
log      =internal               bsize=4096   blocks=2560, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

下面简单解释下字段含义:

  • 第 1 行:
    • isize=512 指定 inode 的容量每个 512 字节大小。
    • agcount=4 是储存区群组的个数,共有 4 个。可以根据 CPU 核心数量进行调整。
    • agsize=1113856 blks 每个储存区群组具有 1113856 个 block。
  • 第 2 行:
    • sectsz=512 指逻辑扇区容量设置为 512 字节。
  • 第 4 行:
    • bsize=4096 指每个 block 大小为 4KB,可以通过 bsize*blocks 得到分区总容量为 17821696KB ≈ 16GB。
    • blocks=4455424 指系统总共有 4455424 个 blocks,可以通过 agsize*agcount 计算得到。
  • 第 5 行:
    • sunit=0 和 RAID 的 stripe 设置有关。
    • swidth=0 和 RAID 的工作盘数量有关。
  • 第 7 行:
    • internal 表示日志区的位置在文件系统内部,而不是外部设备。
    • bsize=4096 日志区 block 大小为 4KB。
    • blocks=2560 日志区 block 总数为 2560,根据此可以计算出日志区大小为 10MB。
  • 第 9 行:
    • extsz=4096 指 extent 大小为 4KB。

文件与目录数据结构

每个文件或目录都会占用一个 inode,且可依据文件内容的大小来分配多个 block 给该文件使用。

目录

当在系统中新建一个目录时,至少会分配一个 inode 与一个 block 给该目录:

  • inode 记录本目录相关权限、属性与本目录 block 号码。
  • block 记录在这个目录下的文件名与该文件(或目录)对应的 inode 号码。

例如使用 ls -i 查看目录 ext333 内文件所占用 inode 号码:

[root@101c7 ext333]$ ls -li
total 16
12 -rw-r--r--. 1 root root     0 Sep  9 15:37 fist.log
11 drwx------. 2 root root 16384 Sep  9 15:10 lost+found

fist.log 文件的 inode 号码是 12,lost+found 目录的 inode 号码是 11,这些就是记录在 ext333 目录 block 中的数据。

再查看一下目录占用的大小:

[root@101c7 ext333]$ ll -dh /ext333/lost+found/ /ext333/
drwxr-xr-x. 3 root root 4.0K Sep  9 15:37 /ext333/
drwx------. 2 root root  16K Sep  9 15:10 /ext333/lost+found/

可以看到 ext333 目录大小为 4KB,而分区时设置的一个 block 大小也是 4KB,因此它使用了 1 个 block 来记录文件和目录 inode 号码信息。另外 lost+found 目录大小 16KB,说明记录信息太多 1 个 block 不够用,分配了 4 个 block 来记录。

文件

当新建一个一般文件时,系统会分配一个 inode 与相对于该文件大小的 block 数量给该文件。

假设 block 大小为 4KB,inode 大小为 128B,要新建 100KB 大小的文件,那么会分配到 1 个 inode 与 25 个 block 来储存该文件。由于 inode 只有 12 个直接指向,因此还要额外分配 1 个 block 来作为 block 号码记录用,文件实际上需要 26 个 block 来储存。

目录树读取

inode 本身并不记录文件名,文件名的记录在目录的 block 当中。所以给目录写权限,实际上操作的是修改目录的 block 数据。

删除文件记录(rm 命令),修改文件指向 inode 记录(mv 命令),或者新增文件指向 inode 记录(cp 命令),对应的就是在目录下进行删除、重命名、新增操作。

以下面记录为例:

[root@101c7 ext333]$ ll -di /ext333/ /ext333/lost+found/ /ext333/lost+found/pass 
2  drwxr-xr-x. 3 root root  4096 Sep  9 15:37 /ext333/
11 drwx------. 2 root root 16384 Sep  9 15:54 /ext333/lost+found/
14 -rw-r--r--. 1 root root     5 Sep  9 15:54 /ext333/lost+found/pass

假设 root 要读取 /ext333/lost+found/pass 这个文件,顺序是这样的:

  1. 通过挂载点的信息找到 inode 号码为 2 的 ext333 目录,查询 2 号 inode 内容,得到目录权限配置为 755。由于存在 x 权限,因此用户可以进入到 ext333 目录中。由于存在 r 权限,因此用户可以读取目录 ext333 的 block 内容。
  2. 通过读取 ext333 目录的 block 内容,得到 lost+found 目录的 inode 号码为 11。继续查询 11 号 inode 内容,得知权限为 rwx,因此继续进入 lost+found 目录,并读取目录的 block 内容。
  3. 通过读取目录 lost+found 的 block 内容得知 pass 文件的 inode 号码为 14。继续查询 14 号 inode 内容,得到权限为 rw。由于存在 r 权限,因此获得数据实际存放的 block 地址。
  4. 读取 pass 文件的 block 内容。

新增文件

新增一个文件或目录时,文件系统的行为如下:

  1. 首先确定用户是否对欲添加文件的目录拥有 w 和 x 权限,只有具备这两个权限才能进行添加操作;
  2. 根据 inode bitmap 寻找一个未使用的 inode 号码,并将新文件的权限和属性写入该 inode 中;
  3. 根据 block bitmap 寻找一个未使用的 block 号码,将实际数据写入该 block 中,并更新 inode 的 block 指向数据;
  4. 同步更新 inode bitmap 和 block bitmap 中的写入的 inode 与 block 数据,并更新 superblock 的内容。

inode table 与 data block 被称为数据存储区域,而其他区域,如 superblock、block bitmap 和 inode bitmap 等,被称为中间区域(metadata)。

写时复制文件系统

写时复制(COW,Copy-On-Write)利用快照兼顾了安全性和性能。如果要修改数据,会使用克隆或可写快照,修改过的数据并不会直接覆盖当前数据,而是被放入文件系统中的另一个位置。即使数据修改已经完成,之前的旧数据也不会被重写。最流行的 COW 文件系统有以下两种:

  • ZFS 文件系统

    ZFS(Z File System)是一个稳定的文件系统,被应用于 Sun 公司的 OpenSolaris 系统中,但它并非开源。开源版本被称为 OpenZFS 项目。

  • Btrfs 文件系统

    Btrfs 文件系统也被称为 B 树文件系统,它稳定且易用,能够动态调整已挂载文件系统的大小。目前,OpenSUSE Linux 将 Btrfs 作为其默认文件系统。