Linux 文件系统层次结构 (FHS) — 目录树的哲学 / The Linux Filesystem Hierarchy Standard
📅 创建时间:2026-06-17 🏷️ 标签:#Linux #FHS #目录结构 #Filesystem Hierarchy Standard 📚 前置知识:[[04-filesystem-concepts]], [[06-linux-boot]]
📋 本章目标
- 理解 FHS (Filesystem Hierarchy Standard) 的设计意图
- 掌握
//boot/home/etc/var/usr/proc/sys/dev/tmp的含义和设计逻辑 - 理解为什么
/boot要单独分区——两个技术原因 - 理解为什么
/home单独分区便于重装 - 理解
/proc和/sys的"伪文件系统"本质 - 理解一切皆文件哲学在
/dev中的最高体现
第1部分:为什么目录结构不是随便设计的?
┌─────────────────────────────────────────────────────────────┐
│ 文件系统层次结构的设计目标 │
├─────────────────────────────────────────────────────────────┤
│ │
│ FHS 的设计目标: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. 可预测性 —— 任何 Linux 发行版, /etc 都是配置, │ │
│ │ /usr/bin 都是可执行文件 │ │
│ │ │ │
│ │ 2. 可共享 vs 不可共享 —— │ │
│ │ /usr 可以在网络中的多台机器间共享 (只读) │ │
│ │ /var 不行 (每台机器自己的运行时数据) │ │
│ │ /etc 不行 (每台机器的配置不同) │ │
│ │ │ │
│ │ 3. 可单独挂载 —— │ │
│ │ /boot 可以是一个单独的分区 │ │
│ │ /home 可以是一个单独的分区 │ │
│ │ /usr 可以是网络挂载 (NFS) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 对比 Windows: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Windows 没有 FHS 的概念 │ │
│ │ C:\Windows\ ← 系统放在这 │ │
│ │ C:\Program Files\ ← 程序放在这 │ │
│ │ C:\Users\ ← 用户数据在这 │ │
│ │ 但这个结构只是"惯例", 没有 FHS 级别的强制标准 │ │
│ │ Windows 靠注册表而不是目录结构来定位组件 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
第2部分:核心目录逐一详解
2.1 / — 根目录,树的起点
┌─────────────────────────────────────────────────────────────┐
│ / 是整个文件系统树的根 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 与 Windows 的关键区别: │
│ │
│ Linux: 单根树 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ / (根, 唯一的) │ │
│ │ ├─ boot/ (可以是单独的分区 nvme0n1p2) │ │
│ │ ├─ home/ (可以是单独的分区 nvme0n1p4) │ │
│ │ ├─ etc/ │ │
│ │ ├─ var/ │ │
│ │ └─ usr/ │ │
│ │ │ │
│ │ 所有分区"挂载"到这棵树的某个位置 │ │
│ │ 用户感知不到分区的边界 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Windows: 多盘符树 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ C: ── Windows │ │
│ │ │ └─ System32 │ │
│ │ D: ── Data │ │
│ │ E: ── 光驱 │ │
│ │ │ │
│ │ 每个盘符是独立的根 │ │
│ │ 用户明显感知到不同的"磁盘" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Linux 单根树的好处: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ mv /home/Jaten/file /var/log/ — 你不需要关心 │ │
│ │ 这两个目录在不在同一个分区上. │ │
│ │ 如果是跨分区: 内核会先复制再删除源 │ │
│ │ 如果同分区: 只改目录项中的 inode 号 (O(1)) │ │
│ │ 但无论哪种方式, 命令都是同一个 mv │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2.2 /boot — 启动的起跑线
┌─────────────────────────────────────────────────────────────┐
│ 为什么 /boot 要单独分区? │
├─────────────────────────────────────────────────────────────┤
│ │
│ 原因1: UEFI ESP 必须是 FAT32 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ UEFI 固件只能读 FAT32 文件系统 │ │
│ │ 但你的根文件系统是 ext4/btrfs │ │
│ │ → ESP (FAT32) 必须是一个独立分区 │ │
│ │ → 挂载到 /boot (或 /boot/efi) │ │
│ │ │ │
│ │ 在你的 Arch 上 (UEFI 模式): │ │
│ │ /boot 分区: FAT32, 512MB │ │
│ │ 里面有: │ │
│ │ /boot/EFI/Linux/grubx64.efi ← GRUB 可执行文件 │ │
│ │ /boot/vmlinuz-linux ← Linux 内核 │ │
│ │ /boot/initramfs-linux.img ← initramfs │ │
│ │ │ │
│ │ 如果你用 BIOS/Legacy 模式: │ │
│ │ /boot 可以用 ext4 │ │
│ │ 但 GRUB 还是只支持部分文件系统 → 通常最终也是 ext4 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 原因2: 根文件系统加密时 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 如果你加密了根分区 (LUKS/btrfs 加密): │ │
│ │ → GRUB 需要在加密之前读取内核 │ │
│ │ → /boot 必须在加密分区之外 │ │
│ │ → 所以 /boot 是未加密的独立分区 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ /boot 里有什么: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ /boot/ │ │
│ │ ├── vmlinuz-linux ← 内核镜像 (bzImage) │ │
│ │ ├── vmlinuz-linux-lts ← LTS 版内核 (备选) │ │
│ │ ├── initramfs-linux.img ← initramfs │ │
│ │ ├── initramfs-linux-fallback.img ← 通用版 initramfs │ │
│ │ ├── grub/ ← GRUB 的模块和配置 │ │
│ │ │ ├── grub.cfg ← 启动菜单 │ │
│ │ │ └── x86_64-efi/ ← GRUB 模块 │ │
│ │ └── EFI/ ← UEFI 启动文件 │ │
│ │ └── Linux/ │ │
│ │ └── grubx64.efi ← UEFI 直接执行的 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
2.3 /home — 用户的城堡
┌─────────────────────────────────────────────────────────────┐
│ 为什么 /home 单独分区? │
├─────────────────────────────────────────────────────────────┤
│ │
│ 单独分区的理由: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 场景: 你 Arch 用了半年, 想换个发行版 (或重装) │ │
│ │ │ │
│ │ 如果 /home 在根分区里: │ │
│ │ 重装 → 根分区被格式化 → 所有个人文件消失 │ │
│ │ │ │
│ │ 如果 /home 是独立分区 (或 btrfs subvolume): │ │
│ │ 重装 → 只格 /, 保留 /home 分区 │ │
│ │ → 安装好系统 → 挂载原来的 /home │ │
│ │ → 你的文档/配置/下载 完好无损 │ │
│ │ │ │
│ │ Arch 的 btrfs 方案更优雅: │ │
│ │ 一个 btrfs 分区 → @ (/) 和 @home (/home) │ │
│ │ 两个 subvolume, 共享同一块磁盘空间 │ │
│ │ 重装时只重建 @, 保留 @home │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ /home 的典型内容: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ /home/Jaten/ │ │
│ │ ├── Documents/ 文档 │ │
│ │ ├── Downloads/ 下载 │ │
│ │ ├── .config/ 用户级应用配置 │ │
│ │ │ ├── nvim/ Neovim 配置 │ │
│ │ │ └── Code/ VSCode 配置 │ │
│ │ ├── .bashrc Bash 配置 (如果没删) │ │
│ │ ├── .ssh/ SSH 密钥 (严格权限 700) │ │
│ │ └── .local/ 用户级安装的程序 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2.4 /etc — 配置的王国
┌─────────────────────────────────────────────────────────────┐
│ /etc — 文本配置 vs Windows 注册表 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Linux 哲学: 配置 = 文本文件 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ /etc/ │ │
│ │ ├── fstab 磁盘挂载配置 │ │
│ │ ├── hosts 主机名→IP 映射 │ │
│ │ ├── resolv.conf DNS 配置 │ │
│ │ ├── pacman.conf Arch 包管理器配置 │ │
│ │ ├── mkinitcpio.conf initramfs 生成配置 │ │
│ │ ├── default/grub GRUB 默认参数 │ │
│ │ ├── systemd/ systemd 服务配置 │ │
│ │ │ └── system/ 用户自定义 service 文件 │ │
│ │ ├── ssh/ SSH 服务配置 │ │
│ │ │ └── sshd_config │ │
│ │ └── passwd 用户数据库 (不是密码!) │ │
│ │ shadow 密码哈希 (只有 root 能读) │ │
│ │ │ │
│ │ 好处: │ │
│ │ • vim → 改配置 → 重启服务 → 生效 │ │
│ │ • git 可以管理 /etc 的变化 │ │
│ │ • 配置可以跨机器复制 │ │
│ │ • 出问题了可以 grep 排查 │ │
│ │ │ │
│ │ Windows 注册表: │ │
│ │ • 二进制数据库 │ │
│ │ • 改配置需要 regedit │ │
│ │ • 不能用 git 管理 │ │
│ │ • 出问题很难 grep │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2.5 /var /usr /tmp — 三种"数据"的三种命运
┌─────────────────────────────────────────────────────────────┐
│ /var /usr /tmp 的区别 │
├─────────────────────────────────────────────────────────────┤
│ │
│ /usr — "Unix System Resources" (以前叫 "User") │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 只读的、可共享的程序和数据 │ │
│ │ │ │
│ │ /usr/bin/ 大部分用户命令 (ls, cat, vim...) │ │
│ │ /usr/lib/ 库文件 + 内核模块 │ │
│ │ /usr/share/ 架构无关的数据 (文档、图标、翻译) │ │
│ │ /usr/local/ 手动编译安装的程序 (包管理器不碰) │ │
│ │ │ │
│ │ 理论上 /usr 可以只读挂载, 或 NFS 共享给多台机器 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ /var — "Variable" (会变化的数据) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 每台机器自己变化的数据, 不能共享 │ │
│ │ │ │
│ │ /var/log/ 系统日志 (journald/syslog) │ │
│ │ /var/cache/ 应用缓存 (pacman 缓存在 /var/cache/pacman/)│ │
│ │ /var/lib/ 运行时状态数据 (数据库、docker...) │ │
│ │ /var/tmp/ 持久化的临时文件 (重启不会清) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ /tmp — "Temporary" (真正的临时文件) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 所有用户共享的临时目录 │ │
│ │ 通常挂载为 tmpfs (内存文件系统) → 重启就清空 │ │
│ │ 权限 1777 (sticky bit) → 谁都能创建, 只能删自己的 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 三种临时目录: │
│ /tmp → 临时, 重启清空 (tmpfs) │
│ /var/tmp → 临时, 重启保留 │
│ /run (tmpfs) → PID文件/锁文件/套接字, 重启清空 │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2.6 /proc /sys /dev — 内核的窗口
┌─────────────────────────────────────────────────────────────┐
│ "伪文件系统" — 一切皆文件 │
├─────────────────────────────────────────────────────────────┤
│ │
│ /proc — 进程和内核信息的窗口 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 这不是真正的文件——是内核在内存里动态生成的数据 │ │
│ │ │ │
│ │ /proc/cpuinfo CPU 详细信息 │ │
│ │ /proc/meminfo 内存使用情况 │ │
│ │ /proc/mounts 当前所有挂载点 │ │
│ │ /proc/partitions 磁盘分区信息 │ │
│ │ /proc/<PID>/ 每个进程的信息 │ │
│ │ ├── cmdline 进程的启动命令 │ │
│ │ ├── environ 进程的环境变量 │ │
│ │ ├── fd/ 进程打开的文件描述符 │ │
│ │ └── maps 进程的内存映射 │ │
│ │ │ │
│ │ 读 /proc 就是问内核: "你能告诉我什么?" │ │
│ │ 写 /proc 就是告诉内核: "请帮我做这件事" │ │
│ │ 例: echo 3 > /proc/sys/vm/drop_caches ← 清缓存 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ /sys — 设备和驱动的结构化信息 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ /sys/block/nvme0n1/ 你的 NVMe 磁盘 │ │
│ │ ├── size 容量 (扇区数) │ │
│ │ ├── queue/scheduler IO 调度器选择 │ │
│ │ └── stat IO 统计 │ │
│ │ │ │
│ │ /sys/class/net/wlan0/ 你的无线网卡 │ │
│ │ /sys/firmware/efi/ UEFI 变量 (之前用来判断运行模式) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ /dev — 设备即文件 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ /dev/nvme0n1 你的 NVMe 磁盘设备 │ │
│ │ /dev/nvme0n1p1 第一个分区 │ │
│ │ /dev/tty 当前终端 │ │
│ │ /dev/null 黑洞——写什么都消失 │ │
│ │ /dev/zero 无限输出 0 │ │
│ │ /dev/random 高质量的随机数 │ │
│ │ /dev/urandom 不阻塞的随机数 │ │
│ │ │ │
│ │ 通过读写这些"文件", 你实际上在和内核驱动对话 │ │
│ │ cat /dev/nvme0n1 → 直接读取磁盘原始内容! │ │
│ │ (当然需要 root 权限) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
第3部分:你的 Arch 上的实际目录树
┌─────────────────────────────────────────────────────────────┐
│ 典型 Arch Linux 安装的挂载方案 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 分区方案 (UEFI + btrfs): │
│ │
│ nvme0n1 │
│ ├── nvme0n1p1 (512M, FAT32) → /boot │
│ ├── nvme0n1p2 (16G, swap) → [SWAP] │
│ └── nvme0n1p3 (460G, btrfs) ┐ │
│ ├─ @ (/) │
│ ├─ @home (/home) │
│ ├─ @log (/var/log) (可选) │
│ └─ @pkg (/var/cache/pacman)│
│ │
│ 最终用户视角 (df -h): │
│ /dev/nvme0n1p1 512M /boot │
│ /dev/nvme0n1p3 460G / │
│ /dev/nvme0n1p3 460G /home (同一个分区, 不同 subvolume) │
│ │
│ 为什么有 @ 前缀? │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ btrfs subvolume 的命名惯例: 用 @ 前缀表示顶级卷 │ │
│ │ 这样在快照/挂载时更容易管理和识别 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
核心总结
总结1:FHS 四大分区理由
/boot 单独: ESP 必须是 FAT32 + 根加密时 GRUB 需要读到内核
/home 单独: 重装系统不丢个人数据
/var 单独: 防止日志写满根分区导致系统不可用
swap 单独: 交换空间不能是文件系统里的一"文件" (有性能开销)2
3
4
总结2:三种"不真实"的目录
/proc → 进程/内核信息 → 内核在内存里现场生成的
/sys → 设备/驱动树 → 内核在内存里现场生成的
/dev → 设备节点 → 内核创建的伪文件, 读写 = 和驱动交互2
3
总结3:Linux vs Windows 配置哲学
Linux: /etc 里文本文件 → vim → 重启服务 → 生效
Windows: 注册表 → regedit → 改键值 → 可能立即生效
差异: 文本 > 二进制, 可掌控 > 方便2
3
章节测试
测试1:单根树
Linux 的"单根树"和 Windows 的"多盘符树"在使用上最大的不同是什么?
测试2:/boot 必须 FAT32
为什么 UEFI 模式下 /boot(或至少 ESP)必须是 FAT32?
测试3:/proc 不是磁盘
ls -l /proc/cpuinfo 显示大小为 0 字节。但 cat /proc/cpuinfo 能输出内容。解释为什么。
测试4:/tmp 的 sticky bit
/tmp 的权限是 drwxrwxrwt。最后一个 t 的含义是什么?
参考答案
测试1答案
Linux 单根树下,所有分区/设备/网络存储都"挂载"到同一棵树的某个位置,用户用 mv 在不同位置间移动文件时不需要关心底层分区。Windows 盘符树(C: D: E:)让每个卷看起来像一棵独立的树,在 C: 和 D: 之间移动文件意味着在不同卷之间搬迁数据。Linux 用 mount 实现了透明的卷边界。
测试2答案
UEFI 固件规范只要求固件自带 FAT32 文件系统驱动。固件 ROM 空间有限(16-32MB),FAT32 是最简单的文件系统(驱动只需几百行 C 代码)。ext4/btrfs 的驱动太复杂,放不进固件 ROM。所以存放 .efi 启动文件的 ESP 分区必须是 FAT32。
测试3答案
/proc/cpuinfo 不在任何磁盘上——它是由内核在读取时实时生成的虚拟文件。因为没有存储在磁盘上的数据结构,所以 ls -l 报告大小为 0。但当你 cat 它时,内核的 proc 文件系统驱动被调用,现场生成 CPU 信息的文本并返回给你。这就是"伪文件系统"的本质。
测试4答案
t(sticky bit,粘滞位)的含义:任何用户都可以在 /tmp 创建文件,但只有文件的所有者和 root 才能删除文件。没有 sticky bit 的话,因为 /tmp 的 w 权限对所有人开放,任何人都可以删除别人的临时文件。sticky bit 让公共目录安全可用。
相关笔记
- [[09-linux-mount]] - fstab 如何定义这些挂载点
- [[10-linux-disk-practice]] - 实战创建这个目录结构
- [[11-windows-disk-fs]] - Windows 用盘符做了完全不同的事
下一步学习
- [ ] 阅读 09 - Linux 挂载机制
学习状态:🟡 开始学习