第 14 章 文件系统 (File Systems)

      +

      核心结论

      • 设备文件:设备以文件形式出现在 /dev(字符设备流式访问、块设备块式访问);mknod 创建设备文件;major/minor 标识驱动。

      • 磁盘与分区:硬盘分区由 fdisk 创建;每个分区可承载文件系统、swap 或裸设备;/proc/partitions 查看分区。

      • 文件系统结构:boot block + superblock + i-node table + data blocks;ext2 进一步划分为 block group;不同文件系统结构略有不同。

      • i-node:每个文件/目录有唯一 i-node;存储类型、权限、owner、size、timestamps、链接数、数据块指针。

      • ext2 数据块寻址:12 个直接指针 + 1 个间接指针 + 1 个双间接指针 + 1 个三间接指针;支持最大约 4TB 文件(4096 块大小)。

      • 挂载/卸载:mount 系统调用挂载文件系统到目录树;umount 卸载;/etc/mtab 和 /proc/mounts 跟踪挂载点。

      • 虚拟文件系统:/proc(内核/进程信息)、/sys(设备 sysfs,udev 基础)、tmpfs(内存文件系统)等都是内核提供的虚拟文件系统。

      本章主旨

      本章是 Linux 文件系统主题的开篇。核心:(1) 设备文件与磁盘结构;(2) 文件系统的内部结构(boot block、superblock、i-node table、data blocks);(3) i-node 内容与 ext2 数据块寻址;(4) 挂载与虚拟文件系统。本章不展开具体文件系统(ext3/4、XFS、Btrfs)的特性对比,那是更专业的内容;不展开链接(详见第 18 章)、目录(详见第 18 章)、ACL(详见第 17 章)。

      一、核心概念

      本章围绕 7 个核心概念展开:从「设备」到「磁盘」到「文件系统结构」到「i-node」再到「挂载」。

      概念 定义 + 重要性 实现提示

      设备文件 (Devices)

      /dev 下的特殊文件;字符设备(流式)和块设备(块式);mknod 创建;major/minor ID 标识驱动。

      §14.1;Linux 2.6+ 用 devtmpfs/udev 动态管理。

      磁盘与分区

      硬盘分为多个分区(fdisk);每个分区可承载文件系统、swap 或裸设备;/proc/partitions 查看。

      §14.2;现代用 parted、gdisk(GPT 替代 MBR)。

      文件系统结构

      boot block + superblock + i-node table + data blocks;ext2 进一步分 block group;不同 FS 略有差异。

      §14.3 Figure 14-1;superblock 是关键元数据。

      i-node (Index Node)

      每个文件/目录的元数据结构;存 type、permissions、owner、size、timestamps、links、数据块指针。

      §14.4;ls -li 显示 i-node 号;i-node 与文件名是「多对一」关系。

      ext2 数据块寻址

      12 个直接指针 + 1 个间接 + 1 个双间接 + 1 个三间接指针;块大小 1024/2048/4096 字节。

      §14.4;最大文件约 4TB(4096 块大小)。

      挂载 (Mount)

      mount 系统调用把文件系统附加到目录树;umount 卸载;/etc/mtab 或 /proc/mounts 跟踪。

      §14.6;挂载点可以是空目录;递归挂载需支持。

      虚拟文件系统 (VFS)

      /proc(内核信息)、/sys(设备 sysfs)、tmpfs(内存)、devtmpfs(设备)、FUSE(用户态 FS)。

      §14.7;统一接口屏蔽底层 FS 差异。

      二、详细笔记

      14.1 设备文件

      What:设备以特殊文件形式出现在 /dev 目录;字符设备流式访问(如键盘、终端),块设备块式访问(如磁盘);内核通过设备驱动管理。

      Why:理解设备文件是理解「为什么 cp 能复制 /dev/zero」「为什么 ls /dev 能看到所有硬件」的前提。

      How

      类型 数据单位 示例

      字符设备

      字节流

      /dev/tty、/dev/null、/dev/zero、/dev/random

      块设备

      固定大小块

      /dev/sda、/dev/nvme0n1

      设备 ID(§14.1):

      • major ID:标识设备类(如 8 = SCSI 磁盘);内核用来查找对应驱动。

      • minor ID:标识同类设备中的具体一个(如 8:0 = 第一块 SCSI 磁盘)。

      • 早期 Linux:major/minor 各 8 位。

      • Linux 2.6+:major 12 位,minor 20 位——支持更多设备。

      设备文件操作(§14.1):

      • mknod(path, mode, dev_t):创建设备文件;需要 CAP_MKNOD

      • mknod 也可创建 FIFO——但用 mkfifo 更专业。

      • 早期 UNIX 还能用 mknod 创建目录——现代用 mkdir

      udev:现代 Linux 用 udev 守护进程根据设备插拔动态创建 /dev 条目;/sys 提供设备信息。

      When

      • 排查硬件——ls -l /dev/sd* 看磁盘设备。

      • 自定义设备驱动——mknod 创建对应节点。

      • 复制磁盘——dd if=/dev/sda of=disk.img

      Example

      # 摘自《The Linux Programming Interface》第 14 章
      $ ls -l /dev/sda /dev/null /dev/tty
      brw-rw---- 1 root disk    8,  0 ... /dev/sda     # 块设备,major=8, minor=0
      crw-rw-rw- 1 root root   1,  3 ... /dev/null    # 字符设备
      crw-rw-rw- 1 root tty    5,  0 ... /dev/tty     # 字符设备

      14.2 磁盘与分区

      What:硬盘分为多个分区(fdisk 命令);每个分区可承载文件系统、swap 或裸设备;分区在内核视为独立块设备。

      Why:理解分区是理解「为什么 /home 在独立分区」「为什么 swap 是单独分区」「怎么扩展磁盘空间」的前提。

      How

      概念 描述

      物理磁盘

      机械硬盘(多盘片旋转)或 SSD;内核看作 /dev/sda 等

      分区表

      MBR(旧,2TB 限制)或 GPT(新,无限制);fdisk/gdisk 管理

      分区类型

      文件系统(ext4/xfs/btrfs)、swap、裸设备(数据库)、LVM

      命名约定

      /dev/sda1 = 第一块 SCSI 磁盘第一分区;/dev/nvme0n1p1 = NVMe 控制器 0 命名空间 1 分区 1

      fdisk -l 列出分区;/proc/partitions 提供程序化访问。

      Swap:

      • mkswap(8) 创建 swap。

      • swapon(8) / swapoff(8) 启用/禁用。

      • /proc/swaps 查看当前 swap。

      When

      • 系统规划——/、/home、/var、swap 分开。

      • 性能优化——SSD 整盘、HDD 单独分区。

      • 排查空间——df -h 看各分区使用。

      14.3 文件系统结构

      What:文件系统由若干部分组成(§14.3 Figure 14-1);ext2 等引入 block group 优化大文件顺序访问。

      Why:理解结构是理解「为什么 superblock 损坏需要修复」「为什么 i-node 用尽」「怎么设计碎片整理」的前提。

      How:通用结构:

      [ boot block ] [ superblock ] [ i-node table ] [ data blocks ]
           0           1              2..N            N+1..end
      部分 内容

      boot block

      第一个块;包含引导信息;多数 FS 不使用

      superblock

      FS 关键参数(块大小、i-node 表大小、FS 大小等);单块关键

      i-node table

      每个文件/目录一个 i-node;记录元数据

      data blocks

      文件和目录的实际内容

      ext2 优化(§14.3):

      • 划分为多个 block group。

      • 每个 block group 包含 superblock 副本、组描述符、i-node 表、数据块。

      • 目标——把小文件相关结构(i-node + 数据)放在同一 block group,减少磁盘寻道。

      When

      • 文件系统损坏——fsck 检查并修复;根据 superblock 修复。

      • 性能调优——大文件分散到多个 block group;小文件聚合。

      • dumpe2fs 查看 ext2/ext3/ext4 详细结构。

      14.4 i-node 详解

      What:i-node 是文件的元数据结构;每个文件/目录有唯一 i-node(在同一 FS 内);i-node 不含文件名(文件名在目录项中)。

      Why:理解 i-node 是理解「为什么硬链接共享 i-node」「为什么文件名可以改但 i-node 不变」「为什么删文件名不一定删数据」的前提。

      How:i-node 内容(§14.4):

      字段 含义

      文件类型

      regular、directory、symlink、char/block device、FIFO、socket

      权限

      9 位 (rwxrwxrwx) + 3 特殊位 (setuid/setgid/sticky)

      链接数

      硬链接数(每加一个硬链接 +1)

      UID/GID

      owner

      大小

      字节数

      时间戳

      atime(访问)、mtime(修改)、ctime(状态变更)

      数据块指针

      指向数据块(ext2 用 12 直接 + 3 间接)

      块数

      实际分配块数(512 字节单位)

      ext2 数据块寻址(§14.4):

      i-node
      +---+---+---+---+---+---+
      | 0 | 1 |...| 11| 12| 13| 14|
      +---+---+---+---+---+---+
        |   |       |   |   |
        |   |       |   |   +-- 三间接(→指针块→指针块→指针块→数据块)
        |   |       |   +------ 双间接(→指针块→指针块→数据块)
        |   |       +---------- 单间接(→指针块→数据块)
        |   +------------------ 数据块 11
        +---------------------- 数据块 0
      • 12 个直接指针 → 直接寻址前 12 个块。

      • 1 个间接指针 → 指向「指针块」(含若干指针);指针块大小 = 块大小 / 4。

      • 1 个双间接指针 → 指向「指针块」的指针块。

      • 1 个三间接指针 → 再深一层。

      最大文件大小(4096 块大小):

      • 12 直接 = 12 × 4096 = 48 KB。

      • 1 间接 = 1024 × 4096 = 4 MB。

      • 1 双间接 = 1024 × 1024 × 4096 = 4 GB。

      • 1 三间接 = 1024 × 1024 × 1024 × 4096 = 4 TB。

      When

      • 看 i-node 号——ls -li

      • 看 i-node 内容——stat file

      • 硬链接共享 i-node——ln source link

      • 删除文件——i-node 链接数 -1;为 0 时回收数据块。

      14.5 文件系统操作

      Whatmkfs 创建文件系统;mount 挂载;umount 卸载;fsck 检查修复。

      Why:理解这些命令是部署 Linux 系统的基础。

      How

      # 摘自《The Linux Programming Interface》第 14 章
      # 创建文件系统
      mkfs.ext4 /dev/sdb1
      
      # 挂载
      mount /dev/sdb1 /mnt
      
      # 卸载
      umount /mnt
      
      # 检查并修复
      fsck /dev/sdb1
      
      # 查看挂载
      mount | column -t
      cat /proc/mounts

      When

      • 新磁盘——mkfs + mount。

      • 系统启动——/etc/fstab 配置自动挂载。

      • 修复——fsck(必须先卸载)。

      14.6 挂载与 umount 系统调用

      Whatmount(src, target, fstype, flags, data) 系统调用挂载文件系统;umount(target) 卸载;mount/umount 命令的底层。

      Why:程序可能需要动态挂载(如自动挂载器 autofs、容器运行时)。

      How

      // 摘自《The Linux Programming Interface》第 14 章
      #include <sys/mount.h>
      
      int mount(const char *source, const char *target,
                const char *filesystemtype, unsigned long mountflags,
                const void *data);
      // source: 设备路径(如 "/dev/sdb1")或网络路径
      // target: 挂载点目录(必须存在)
      // filesystemtype: "ext4", "nfs", "tmpfs" 等
      // mountflags: MS_RDONLY / MS_NOSUID / MS_NODEV / MS_NOEXEC / MS_SYNCHRONOUS 等
      // data: FS 特定选项字符串
      // 成功返回 0;需要 CAP_SYS_ADMIN
      
      int umount(const char *target);
      // 卸载;target 是挂载点或设备
      // 成功返回 0

      When

      • 自动挂载器(autofs、udisks)。

      • 容器运行时(Docker/Podman)。

      • 嵌入式系统按需挂载 SD 卡。

      Example

      // 摘自《The Linux Programming Interface》第 14 章
      if (mount("/dev/sdb1", "/mnt/usb", "ext4", 0, NULL) == -1)
          errExit("mount");
      // ... 使用 /mnt/usb ...
      if (umount("/mnt/usb") == -1)
          errExit("umount");

      14.7 虚拟文件系统

      What:Linux 支持多种虚拟文件系统(VFS);不占磁盘;由内核动态生成内容。

      Why:理解 VFS 是理解 /proc、/sys、/dev 的基础。

      How

      VFS 挂载点

      内容

      proc

      /proc

      进程/内核信息(详见第 12 章)

      sysfs

      /sys

      设备、驱动、内核对象(udev 基础)

      devtmpfs

      /dev

      设备文件(内核自动管理)

      tmpfs

      /tmp, /run

      内存文件系统(关机丢失)

      FUSE

      /mnt/fuse

      用户态文件系统(无需内核模块)

      /proc/filesystems 列出已注册的文件系统类型。

      When

      • /proc 调试、监控。

      • /sys 设备配置、容器。

      • tmpfs 临时文件、性能敏感场景。

      三、关键图表

      非可视化条目(FS 关键概念速查)
      项目 描述

      字符设备 vs 块设备

      流式(终端)vs 块式(磁盘);都通过 /dev 访问

      mknod

      创建设备文件;需要 CAP_MKNOD

      major/minor

      12/20 位;驱动 + 设备标识

      superblock

      FS 关键参数;损坏需 fsck 修复

      i-node

      文件元数据;不含文件名

      ext2 寻址

      12 直接 + 1 间接 + 1 双间接 + 1 三间接

      块大小

      1024/2048/4096 字节;影响最大文件大小

      mount/umount

      系统调用;需 CAP_SYS_ADMIN

      虚拟 FS

      proc / sysfs / tmpfs / devtmpfs / FUSE

      四、思维导图

      mindmap
        root((第 14 章 文件系统))
          设备
            字符块
            major minor
            mknod
            udev
          磁盘分区
            fdisk gdisk
            MBR GPT
            swap
            proc partitions
          FS 结构
            boot superblock
            i-node table
            data blocks
            block group
          i node
            元数据
            type perms size
            timestamps
            数据块指针
          ext2 寻址
            12 直接
            单间接
            双间接
            三间接
            4TB 最大
          挂载
            mount 系统调用
            umount
            fstab
            自动挂载
          虚拟 FS
            proc sysfs
            tmpfs devtmpfs
            FUSE

      五、重点与易错点

      1. i-node 不含文件名:文件名在目录项中;硬链接共享 i-node 但文件名不同。

      2. 删除文件名 ≠ 删除文件:i-node 链接数 -1;为 0 才释放数据块。

      3. ext2/3/4 块大小 4096 最常见:影响最大文件大小、性能;mkfs 时指定。

      4. superblock 损坏 = FS 损坏:内核有 superblock 备份(block group 头部);fsck 可修复。

      5. 设备文件 major/minor 不等同于文件名/dev/sda1 是约定名;内核按 major/minor 找驱动。

      6. 挂载前必须先存在挂载点目录:mount 不会自动创建;umount 不删除挂载点目录。

      7. 移动设备不能 umount 时:检查「文件被占用」;lsof | grep mountpoint 查占用进程。

      8. FUSE 让用户态实现 FS:NFS、S3、sshfs 都用 FUSE;不需要内核模块。

      9. /proc 是虚拟 FS:不占磁盘;/proc/PID 进程终止即消失。

      10. Linux 2.6+ major/minor 扩展到 12/20 位:避免设备 ID 耗尽。

      11. 跨章衔接:第 15 章展开文件属性(stat);第 17 章展开 ACL;第 18 章展开目录和链接;本章是 FS 结构基础。