第 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; |
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 文件系统操作
What:mkfs 创建文件系统;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 系统调用
What:mount(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 关键概念速查)
|
四、思维导图
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
五、重点与易错点
-
i-node 不含文件名:文件名在目录项中;硬链接共享 i-node 但文件名不同。
-
删除文件名 ≠ 删除文件:i-node 链接数 -1;为 0 才释放数据块。
-
ext2/3/4 块大小 4096 最常见:影响最大文件大小、性能;mkfs 时指定。
-
superblock 损坏 = FS 损坏:内核有 superblock 备份(block group 头部);fsck 可修复。
-
设备文件 major/minor 不等同于文件名:
/dev/sda1是约定名;内核按 major/minor 找驱动。 -
挂载前必须先存在挂载点目录:mount 不会自动创建;umount 不删除挂载点目录。
-
移动设备不能 umount 时:检查「文件被占用」;
lsof | grep mountpoint查占用进程。 -
FUSE 让用户态实现 FS:NFS、S3、sshfs 都用 FUSE;不需要内核模块。
-
/proc 是虚拟 FS:不占磁盘;
/proc/PID进程终止即消失。 -
Linux 2.6+ major/minor 扩展到 12/20 位:避免设备 ID 耗尽。
-
跨章衔接:第 15 章展开文件属性(stat);第 17 章展开 ACL;第 18 章展开目录和链接;本章是 FS 结构基础。