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