强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Unix 设计哲学教程 / 第 10 章:文件系统层次

第 10 章:文件系统层次

“In Unix, the file system is the single most important abstraction.”

Unix 文件系统的层次结构(Filesystem Hierarchy)是 Unix 设计哲学的集中体现。每一个目录都有明确的用途,每个文件都有确定的位置。本章深入讲解 FHS 标准、挂载机制和权限模型。


10.1 文件系统层次标准(FHS)

目录结构概览

/                          根目录(所有目录的起点)
├── bin/                   基本命令(所有用户可用)
│   ├── ls, cp, mv, rm     核心工具
│   ├── bash               Shell
│   └── cat, grep, sed     文本处理
├── sbin/                  系统管理命令
│   ├── fdisk              磁盘分区
│   ├── iptables           防火墙
│   └── systemctl          服务管理
├── boot/                  启动文件
│   ├── vmlinuz-*          内核镜像
│   ├── initrd/initramfs   初始 RAM 磁盘
│   └── grub/              引导加载器
├── dev/                   设备文件
│   ├── sda, nvme0n1       块设备
│   ├── tty, pts/*         终端
│   ├── null, zero, random 特殊设备
│   └── shm/               共享内存
├── etc/                   系统配置
│   ├── hostname           主机名
│   ├── passwd, shadow     用户信息
│   ├── fstab              文件系统表
│   ├── ssh/               SSH 配置
│   ├── nginx/             Nginx 配置
│   └── systemd/           systemd 配置
├── home/                  用户主目录
│   ├── alice/
│   └── bob/
├── lib/, lib64/           共享库
│   ├── libc.so.6          C 标准库
│   ├── libpthread.so      POSIX 线程库
│   └── modules/           内核模块
├── media/                 可移动媒体挂载点
├── mnt/                   临时挂载点
├── opt/                   第三方软件
│   ├── google/chrome/
│   └── company/app/
├── proc/                  进程信息(虚拟)
├── root/                  root 用户的主目录
├── run/                   运行时数据
│   ├── pid 文件
│   └── socket 文件
├── srv/                   服务数据
├── sys/                   设备信息(虚拟)
├── tmp/                   临时文件(重启后可能清空)
├── usr/                   用户级程序和数据
│   ├── bin/               用户命令
│   ├── sbin/              系统管理命令
│   ├── lib/               库文件
│   ├── share/             架构无关数据
│   │   ├── man/           手册页
│   │   ├── doc/           文档
│   │   └── locale/        国际化数据
│   ├── include/           头文件
│   └── local/             本地安装的软件
│       ├── bin/
│       ├── lib/
│       └── share/
└── var/                   可变数据
    ├── log/               日志文件
    ├── cache/             缓存
    ├── lib/               应用状态数据
    ├── spool/             队列数据(邮件、打印)
    ├── tmp/               持久化临时文件
    └── www/               Web 服务器数据

各目录的用途总结

目录用途生命周期典型大小
/bin基本命令随系统安装10-100 MB
/sbin系统管理命令随系统安装5-50 MB
/etc系统配置修改后持久化10-100 MB
/home用户数据用户管理可变
/var可变数据运行时增长可变(可能很大)
/tmp临时文件重启后可能清空可变
/usr用户级程序随系统和软件包1-10 GB
/opt第三方软件手动管理可变
/proc进程信息内存文件系统0(虚拟)
/sys设备信息内存文件系统0(虚拟)
/dev设备文件自动生成很小

/usr 与 / 的历史

/usr 目录的历史
├── 1970 年代:/usr 是用户主目录("user" 的缩写)
│   ├── /usr/alice/
│   └── /usr/bob/
├── 1980 年代:/usr 成为系统程序目录
│   ├── /usr/bin/    — 用户命令
│   ├── /usr/lib/    — 库文件
│   └── /usr/include/ — 头文件
└── 现代(UsrMerge):/bin → /usr/bin 的符号链接
    ├── Fedora 17+ 已合并
    ├── Debian 12+ 已合并
    ├── Ubuntu 22.04+ 已合并
    └── 好处:简化文件系统,减少碎片
# 检查是否已进行 UsrMerge
ls -la /bin
# 如果是符号链接 → lrwxrwxrwx 1 root root 7 May 10 10:00 bin -> usr/bin
# 如果是目录 → drwxr-xr-x 2 root root 4096 May 10 10:00 bin

10.2 挂载点(Mount Point)

挂载的概念

Unix 的挂载机制

物理磁盘/分区是一个设备文件(如 /dev/sda1)
挂载就是将这个设备的文件系统"附加"到目录树的某个点

挂载前:
/                       ← 在 /dev/sda1 上
├── bin/
├── etc/
└── home/

挂载 /dev/sdb1 到 /data 后:
/                       ← 在 /dev/sda1 上
├── bin/
├── etc/
├── home/
└── data/               ← /dev/sdb1 的文件系统
    ├── projects/
    └── backups/
# 查看当前挂载
mount
df -Th

# 挂载文件系统
sudo mount /dev/sdb1 /data                    # 挂载分区
sudo mount -t nfs server:/share /mnt/nfs      # 挂载 NFS
sudo mount -t tmpfs tmpfs /tmp/ramdisk         # 挂载内存文件系统
sudo mount -o loop image.iso /mnt/iso          # 挂载 ISO 镜像

# 卸载
sudo umount /data
sudo umount /mnt/nfs

# 挂载选项
mount -o ro /dev/sdb1 /data                    # 只读
mount -o rw,noexec,nosuid /dev/sdb1 /data      # 读写,禁止执行,禁止 SUID
mount -o remount,rw /                          # 重新挂载为读写

# /etc/fstab —— 启动时自动挂载
cat /etc/fstab
# <设备>        <挂载点>  <类型>  <选项>              <dump> <fsck>
# UUID=xxx      /          ext4   errors=remount-ro    0      1
# UUID=yyy      /data      xfs    defaults,noatime     0      2
# UUID=zzz      /boot/efi  vfat   umask=0077           0      1
# tmpfs         /tmp       tmpfs  defaults,noatime     0      0

# 获取 UUID
blkid /dev/sda1
# /dev/sda1: UUID="xxxx-xxxx" TYPE="ext4"

# 测试 fstab 配置
sudo mount -a  # 挂载 fstab 中所有未挂载的条目

绑定挂载(Bind Mount)

# 绑定挂载:将一个目录映射到另一个位置
sudo mount --bind /home/user/projects /srv/www/projects

# 常见用途:
# 1. chroot 环境中提供 /proc, /dev
sudo mount --bind /proc chroot_dir/proc
sudo mount --bind /dev chroot_dir/dev

# 2. Docker 容器中的卷映射
docker run -v /host/path:/container/path image

# 3. 共享目录到不同位置
sudo mount --bind /data/shared /home/alice/shared
sudo mount --bind /data/shared /home/bob/shared

tmpfs 与 RAM 磁盘

# tmpfs: 基于内存的文件系统
# 优点:极快的 I/O
# 缺点:重启后数据丢失,受内存限制

# 创建 tmpfs
sudo mount -t tmpfs -o size=1G tmpfs /mnt/ramdisk

# 常见的 tmpfs 挂载点
df -Th | grep tmpfs
# tmpfs    tmpfs   7.8G  1.2M  7.8G   1% /dev/shm
# tmpfs    tmpfs   7.8G  1.5M  7.8G   1% /run
# tmpfs    tmpfs   7.8G     0  7.8G   0% /sys/fs/cgroup
# tmpfs    tmpfs   1.6G  4.0K  1.6G   1% /run/user/1000

# /dev/shm 是默认的共享内存文件系统
# 常用于进程间共享数据
ls -la /dev/shm/

10.3 权限模型

基本权限

Unix 文件权限模型

每个文件有三组权限:
├── 所有者(User/Owner)—— 文件的拥有者
├── 所属组(Group)—— 文件所属的用户组
└── 其他(Others)—— 除以上两者外的所有人

每组有三种权限:
├── r (read)    — 读取:查看文件内容 / 列出目录内容
├── w (write)   — 写入:修改文件内容 / 在目录中创建/删除文件
└── x (execute) — 执行:运行文件 / 进入目录

权限用数字表示:
├── r = 4
├── w = 2
├── x = 1
└── 组合:rwx = 7, rw- = 6, r-x = 5, r-- = 4
# 查看文件权限
ls -la /etc/passwd
# -rw-r--r-- 1 root root 2345 May 10 10:00 /etc/passwd
# ↑          ↑     ↑     ↑
# 权限       链接数 所有者 所属组

# 权限解读
# -rw-r--r--
# │││││││││
# │││││││└┴── 其他: r-- (只读)
# ││││││└──── 所属组: r-- (只读)
# │└┴┴┴────── 所有者: rw- (读写)
# └────────── 文件类型: - (普通文件)

# 修改权限
chmod 755 script.sh           # rwxr-xr-x
chmod 644 config.txt          # rw-r--r--
chmod u+x script.sh           # 给所有者添加执行权限
chmod go-w file.txt            # 移除组和其他的写权限
chmod a+r file.txt             # 给所有人添加读权限

# 符号模式
# u = 所有者, g = 所属组, o = 其他, a = 所有人
# + = 添加, - = 移除, = = 设置
chmod u=rwx,g=rx,o=r file.txt  # rwxr-xr--
chmod o= file.txt              # 移除其他人的所有权限

特殊权限位

# SUID (Set User ID) — 以文件所有者身份执行
chmod u+s /usr/bin/passwd
ls -la /usr/bin/passwd
# -rwsr-xr-x 1 root root 68208 May 10 10:00 /usr/bin/passwd
#    ↑ s 表示 SUID
# 典型用例:passwd 命令需要 root 权限修改 /etc/shadow

# SGID (Set Group ID) — 以文件所属组身份执行
chmod g+s /shared/dir
# drwxrwsr-x 2 root staff 4096 May 10 10:00 /shared/dir
#       ↑ s 表示 SGID
# 对目录的效果:新文件继承目录的组

# Sticky Bit — 只有所有者能删除文件
chmod +t /tmp
ls -ld /tmp
# drwxrwxrwt 2 root root 4096 May 10 10:00 /tmp
#          ↑ t 表示 Sticky Bit
# 典型用例:/tmp 目录所有人都能写入,但只能删除自己的文件

# 数字表示特殊权限
chmod 4755 file    # SUID
chmod 2755 file    # SGID
chmod 1755 file    # Sticky Bit
chmod 6755 file    # SUID + SGID

umask

# umask 控制新创建文件的默认权限
# 文件默认权限 = 666 - umask
# 目录默认权限 = 777 - umask

umask 022
# 文件: 666 - 022 = 644 (rw-r--r--)
# 目录: 777 - 022 = 755 (rwxr-xr-x)

umask 077
# 文件: 666 - 077 = 600 (rw-------)
# 目录: 777 - 077 = 700 (rwx------)

# 查看当前 umask
umask
# 0022

# 设置 umask
umask 027  # 当前 Shell 生效

# 持久化
echo "umask 027" >> ~/.bashrc

10.4 文件所有权

用户和组

# 查看当前用户
whoami
id
# uid=1000(alice) gid=1000(alice) groups=1000(alice),27(sudo),100(users)

# 查看用户信息
cat /etc/passwd | head -5
# root:x:0:0:root:/root:/bin/bash
# alice:x:1000:1000:Alice:/home/alice:/bin/bash

# 查看组信息
cat /etc/group | head -5
# root:x:0:
# sudo:x:27:alice
# users:x:100:

# 修改文件所有者
sudo chown alice file.txt            # 修改所有者
sudo chown alice:staff file.txt      # 修改所有者和组
sudo chown :staff file.txt           # 只修改组
sudo chown -R alice:staff /data/     # 递归修改

# 修改文件组
sudo chgrp staff file.txt

# 查看用户的组
groups alice
id alice

ACL(访问控制列表)

# 当基本权限不够用时,可以使用 ACL

# 查看 ACL
getfacl file.txt

# 设置 ACL
setfacl -m u:bob:rw file.txt         # 给 bob 用户读写权限
setfacl -m g:staff:r file.txt        # 给 staff 组读权限
setfacl -m o::--- file.txt           # 移除其他人的所有权限

# 默认 ACL(对目录,新文件自动继承)
setfacl -d -m u:bob:rw /shared/

# 删除 ACL
setfacl -x u:bob file.txt            # 删除特定 ACL
setfacl -b file.txt                  # 删除所有 ACL

# 递归设置
setfacl -R -m u:bob:rw /data/

10.5 日志文件系统

日志的作用

传统文件系统 vs 日志文件系统

传统文件系统(如 ext2):
1. 写入数据块
2. 更新元数据(inode、位图)
3. 如果在步骤 2 时断电 → 文件系统不一致
4. 需要 fsck 全盘扫描(大文件系统可能需要数小时)

日志文件系统(如 ext4、XFS):
1. 先将变更写入日志区域(Journal)
2. 标记日志已完成
3. 执行实际的数据写入
4. 标记数据写入已完成
5. 如果在任何步骤断电 → 从日志恢复(秒级)

ext4 文件系统

# ext4 是 Linux 最常用的文件系统

# 创建 ext4 文件系统
sudo mkfs.ext4 /dev/sdb1

# 带标签创建
sudo mkfs.ext4 -L "DATA" /dev/sdb1

# 调整大小
sudo resize2fs /dev/sdb1

# 文件系统检查
sudo e2fsck -f /dev/sdb1

# 查看文件系统信息
sudo tune2fs -l /dev/sdb1
# 或
sudo dumpe2fs /dev/sdb1 | head -50

# ext4 特性
# - 日志
# - 延迟分配
# - 区段(Extent)
# - 在线碎片整理
# - 最大文件大小 16TB
# - 最大文件系统大小 1EB

XFS 文件系统

# XFS:高性能文件系统,特别适合大文件

# 创建 XFS 文件系统
sudo mkfs.xfs /dev/sdb1

# 带参数创建
sudo mkfs.xfs -f -d agcount=32 /dev/sdb1

# 查看信息
sudo xfs_info /dev/sdb1

# 碎片整理
sudo xfs_fsr /data

# 扩大 XFS(只能扩大,不能缩小)
sudo xfs_growfs /data

# 修复
sudo xfs_repair /dev/sdb1

# XFS 特性
# - 高性能并行 I/O
# - 动态 inode 分配
# - 在线备份(xfsdump/xfsrestore)
# - 最大文件系统大小 8EB

Btrfs 文件系统

# Btrfs:下一代 Linux 文件系统

# 创建
sudo mkfs.btrfs /dev/sdb1

# 子卷管理
sudo btrfs subvolume create /data/@home
sudo btrfs subvolume create /data/@snapshots

# 快照
sudo btrfs subvolume snapshot /data/@home /data/@snapshots/home-$(date +%Y%m%d)

# 压缩
sudo mount -o compress=zstd /dev/sdb1 /data

# 查看信息
sudo btrfs filesystem show /data
sudo btrfs filesystem usage /data

# Btrfs 特性
# - 写时复制(CoW)
# - 快照和克隆
# - 内置 RAID(0, 1, 10, 5, 6)
# - 数据校验(checksum)
# - 在线压缩
# - 发送/接收(增量备份)

10.6 磁盘管理

分区

# 查看磁盘和分区
lsblk                         # 块设备列表
fdisk -l                      # 所有磁盘的分区表
parted /dev/sda print         # 使用 parted 查看

# 分区工具
# fdisk — MBR 分区(传统)
# gdisk — GPT 分区(现代,推荐)
# parted — 脚本友好

# 使用 fdisk 分区
sudo fdisk /dev/sdb
# n — 新建分区
# d — 删除分区
# p — 打印分区表
# t — 更改分区类型
# w — 写入并退出

# 使用 parted 分区(脚本化)
sudo parted /dev/sdb --script \
    mklabel gpt \
    mkpart primary ext4 0% 50% \
    mkpart primary xfs 50% 100%

# 格式化
sudo mkfs.ext4 /dev/sdb1
sudo mkfs.xfs /dev/sdb2

# 更新内核分区表
sudo partprobe /dev/sdb

LVM(逻辑卷管理)

LVM 的层次结构

物理卷 (PV) → 卷组 (VG) → 逻辑卷 (LV) → 文件系统

┌──────────┐   ┌──────────┐   ┌──────────┐
│ /dev/sdb │   │ /dev/sdc │   │ /dev/sdd │
│    PV    │   │    PV    │   │    PV    │
└────┬─────┘   └────┬─────┘   └────┬─────┘
     │              │              │
     └──────┬───────┴──────┬───────┘
            │              │
      ┌─────┴─────┐  ┌────┴────┐
      │    VG0    │  │   VG1   │
      └─────┬─────┘  └────┬────┘
            │              │
    ┌───────┼───────┐      │
    │       │       │      │
 ┌──┴──┐┌──┴──┐┌──┴──┐ ┌──┴──┐
 │ LV1 ││ LV2 ││ LV3 │ │ LV4 │
 │ 50G ││ 100G││ 200G│ │ 500G│
 └─────┘└─────┘└─────┘ └─────┘
# LVM 操作
# 1. 创建物理卷
sudo pvcreate /dev/sdb /dev/sdc /dev/sdd
sudo pvs

# 2. 创建卷组
sudo vgcreate vg_data /dev/sdb /dev/sdc /dev/sdd
sudo vgs

# 3. 创建逻辑卷
sudo lvcreate -L 100G -n lv_web vg_data
sudo lvcreate -l 100%FREE -n lv_db vg_data
sudo lvs

# 4. 格式化和挂载
sudo mkfs.ext4 /dev/vg_data/lv_web
sudo mount /dev/vg_data/lv_web /var/www

# 5. 扩展逻辑卷(在线扩容!)
sudo lvextend -L +50G /dev/vg_data/lv_web
sudo resize2fs /dev/vg_data/lv_web

# 6. 快照
sudo lvcreate -s -L 10G -n snap_web /dev/vg_data/lv_web

10.7 磁盘配额

# 启用配额
# 在 /etc/fstab 中添加 usrquota,grpquota
# UUID=xxx /data ext4 defaults,usrquota,grpquota 0 2

# 重新挂载
sudo mount -o remount /data

# 初始化配额
sudo quotacheck -cugm /data
sudo quotaon /data

# 设置用户配额
sudo edquota -u alice
# 或使用 setquota
sudo setquota -u alice 10G 12G 0 0 /data

# 查看配额
quota -u alice
repquota /data

# 设置宽限时间
sudo edquota -t

注意事项

  1. 不要修改 /proc/sys 的持久化:这些是虚拟文件系统,重启后修改会丢失。需要持久化的配置应使用 /etc/sysctl.conf 或 udev 规则。
  2. 谨慎使用 chmod 777:这会给所有人完全权限,是安全隐患。应该使用最小权限原则。
  3. SUID 程序有安全风险:SUID 程序以所有者身份执行,如果程序有漏洞,可能被利用获得更高权限。尽量减少 SUID 程序数量。
  4. fstab 错误可能导致系统无法启动:修改 fstab 后,使用 mount -a 测试,避免在 fstab 中写错设备 UUID。
  5. 选择文件系统时考虑场景:ext4 适合通用场景,XFS 适合大文件和高并发,Btrfs 适合需要快照和数据校验的场景。

扩展阅读