第 2 章

Linux 文件系统深度

第2章:Linux 文件系统深度解析

Linux 的"一切皆文件"哲学,让你只需理解文件系统就能掌握整个系统的行为。本章从 FHS 目录结构出发,深入 inode 内部原理,掌握硬链接与软链接的本质区别,再到 find 的高级用法和磁盘使用分析——这是所有 Shell 高手的必备知识体系。

2.1 FHS:文件系统层次结构标准

FHS(Filesystem Hierarchy Standard) 规定了 Linux 系统目录结构的标准,确保不同发行版的目录布局保持一致。理解每个目录的用途是系统管理的基础。

目录 全称/含义 典型内容
/ 根目录 整个文件系统的起点,所有路径从此开始
/bin 基本用户二进制文件 ls, cp, mv, cat, bash(现代系统常为 /usr/bin 的符号链接)
/sbin 系统管理二进制文件 fdisk, ifconfig, fsck, init(需要 root 权限的命令)
/usr Unix 系统资源 /usr/bin(用户程序),/usr/lib(库文件),/usr/share(共享数据)
/etc 系统配置文件 /etc/passwd, /etc/fstab, /etc/nginx/, /etc/ssh/(所有系统级配置)
/var 可变数据 /var/log(日志),/var/spool(队列),/var/cache(缓存),/var/www(Web 根目录)
/tmp 临时文件(重启清空) 所有用户都可写,常挂载为 tmpfs(内存)
/proc 进程信息虚拟文件系统 /proc/cpuinfo, /proc/meminfo, /proc/[PID]/(内核数据接口)
/sys 系统设备虚拟文件系统 内核导出的设备/驱动/电源管理接口(sysfs)
/dev 设备文件 /dev/sda(磁盘),/dev/null,/dev/zero,/dev/tty(终端)
/home 用户主目录 /home/alice/, /home/bob/(每个用户的私人空间)
/root root 用户的主目录 仅 root 可访问,不在 /home 下
/opt 可选第三方软件 手动安装的大型软件包(如 /opt/google/chrome)
/srv 服务数据 Web 服务、FTP 服务的数据目录(/srv/www, /srv/ftp)
/boot 启动加载文件 vmlinuz, initrd.img, grub/(内核和 GRUB 配置)
/lib 基本共享库 /lib/x86_64-linux-gnu/libc.so.6(C 标准库等)
/mnt 临时挂载点 管理员临时挂载设备的约定目录
/media 可移动媒体挂载点 自动挂载的 USB、DVD 等(/media/username/USB)
# 查看根目录结构
ls -la /

# 了解各目录大小
du -sh /* 2>/dev/null | sort -hr | head -20

# 查看当前挂载的文件系统
findmnt
# 或
mount | column -t

# 查看磁盘分区和文件系统类型
lsblk -f

2.2 inode 原理:文件的真实身份

在 Linux 文件系统中,文件名只是指向 inode 的一个标签,真正存储文件元数据的是 inode(Index Node,索引节点)。每个文件(包括目录)都对应一个唯一的 inode 编号。

inode 存储的内容(ASCII 结构图)

┌─────────────────────────────────────────────┐
│                   inode                     │
├─────────────────────────────────────────────┤
│  inode number    : 2097152                  │
│  file type       : regular file (-)         │
│  permissions     : 0644 (rw-r--r--)         │
│  link count      : 1  (硬链接数量)          │
│  owner UID       : 1000 (alice)             │
│  group GID       : 1000 (alice)             │
│  file size       : 4096 bytes               │
│  atime           : 2026-04-25 10:00:00      │  ← 最后访问时间
│  mtime           : 2026-04-20 14:30:00      │  ← 最后修改时间(内容)
│  ctime           : 2026-04-21 09:00:00      │  ← 最后改变时间(元数据)
│  block size      : 4096                     │
│  block count     : 8                        │
│  direct blocks   : [ptr0][ptr1]...[ptr11]   │  ← 直接块指针(12个)
│  single indirect : [ptr → block table]      │  ← 一级间接块
│  double indirect : [ptr → ptr → blocks]     │  ← 二级间接块
│  triple indirect : [ptr → ptr → ptr → ...]  │  ← 三级间接块
└─────────────────────────────────────────────┘
         ↑
         目录项(dentry)中存储:文件名 → inode 编号
         文件名本身不在 inode 中!

关键理解: inode 不存储文件名——文件名存储在目录项(directory entry)中,目录项将文件名映射到 inode 编号。这就是为什么同一个文件可以有多个名字(硬链接),也是为什么移动文件(mv)在同一文件系统内瞬间完成——只需修改目录项,不移动数据。

# 查看文件的 inode 编号
ls -i filename.txt
# 2097152 filename.txt

# 查看目录内所有文件的 inode 号
ls -li /etc/

# 查看 inode 使用情况(每个文件系统的 inode 总数和已用数)
df -i
# Filesystem       Inodes  IUsed  IFree IUse% Mounted on
# /dev/sda1      6553600  87234 6466366    2% /

# 查找 inode 用尽的迹象(inode 满了但磁盘空间还有)
df -i | awk '$5 == "100%"'

# 查找 inode 号为某个值的文件
find / -inum 2097152 2>/dev/null

2.3 stat 命令:读取文件元数据

stat 命令直接读取并显示文件的 inode 信息,是了解文件真实状态的最佳工具。

$ stat /etc/passwd
  File: /etc/passwd
  Size: 2847            Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d    Inode: 524299      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2026-04-25 08:12:33.421076800 +0800   ← atime:最后读取时间
Modify: 2026-04-20 14:22:10.123456789 +0800   ← mtime:内容最后修改时间
Change: 2026-04-20 14:22:10.123456789 +0800   ← ctime:元数据最后修改时间

# 三个时间戳的区别:
# atime (access time)  → 每次读取文件时更新
#                        注意:挂载时使用 noatime 选项可禁止更新(提升性能)
# mtime (modify time)  → 文件内容改变时更新(写入数据)
# ctime (change time)  → 文件元数据改变时更新(权限/所有者/链接数)
#                        注意:ctime 不能被人为修改(touch 不影响 ctime)

# 只查看特定字段
stat -c "%n: inode=%i, size=%s, mtime=%y" /etc/passwd

# 格式化输出
stat --format="File: %n | Size: %s bytes | Permissions: %A | Inode: %i" /etc/passwd

# 查看目录的 stat
stat /var/log/

# 使用 touch 修改 atime 和 mtime
touch -a file.txt          # 只更新 atime
touch -m file.txt          # 只更新 mtime
touch -t 202601010000 file.txt  # 设置为指定时间

2.4 Linux 文件类型详解

ls -l 输出第一列的第一个字符表示文件类型。Linux 共有 7 种文件类型:

# 文件类型标识符(ls -l 第一列首字符):
# -  普通文件(regular file):文本、二进制、图片等
# d  目录(directory)
# l  软链接(symbolic link)
# c  字符设备(character device):/dev/tty, /dev/null(逐字节 I/O)
# b  块设备(block device):/dev/sda, /dev/loop0(块 I/O,如磁盘)
# p  命名管道(FIFO):进程间通信
# s  Unix 域套接字(socket):/run/docker.sock

# 实际示例:
ls -la /dev/null /dev/sda /tmp /run/docker.sock
# crw-rw-rw-  1 root root 1, 3 ... /dev/null     ← c 字符设备
# brw-rw----  1 root disk 8, 0 ... /dev/sda      ← b 块设备
# drwxrwxrwt 20 root root ...    /tmp             ← d 目录
# srw-rw----  1 root docker ...  /run/docker.sock ← s 套接字

# 用 file 命令识别文件真实类型(不依赖扩展名)
file /bin/ls
# /bin/ls: ELF 64-bit LSB pie executable, x86-64 ...

file /etc/passwd
# /etc/passwd: ASCII text

file /dev/sda
# /dev/sda: block special (8/0)

# 用 find 按文件类型搜索
find /dev -type c     # 所有字符设备
find /tmp -type p     # 所有命名管道
find /run -type s     # 所有套接字

# 创建命名管道
mkfifo /tmp/mypipe
ls -l /tmp/mypipe
# prw-r--r-- 1 user user 0 ... /tmp/mypipe      ← p 管道

2.5 硬链接 vs 软链接:本质区别

链接是 Linux 文件系统中的核心概念,理解硬链接(hard link)和软链接(symbolic link)的区别,是理解 inode 原理的最佳实践场景。

# === 硬链接(Hard Link)===
# 创建硬链接:两个文件名指向同一个 inode
ln original.txt hardlink.txt

# 验证:两者 inode 号相同
ls -li original.txt hardlink.txt
# 2097152 -rw-r--r-- 2 user user 100 ... original.txt
# 2097152 -rw-r--r-- 2 user user 100 ... hardlink.txt
#   ↑ 相同 inode        ↑ 链接数为 2

# 删除原文件——数据依然存在(链接数减 1,不为 0 时不删除数据)
rm original.txt
cat hardlink.txt  # 依然可以读取!

# 硬链接的限制:
# 1. 不能跨文件系统(inode 号仅在同一文件系统内唯一)
# 2. 不能对目录创建硬链接(防止循环引用)

# === 软链接(Symbolic Link)===
# 创建软链接:存储目标路径字符串
ln -s /path/to/original.txt symlink.txt
ln -s /usr/bin/python3 /usr/local/bin/python  # 常见用法

# 验证:inode 号不同,l 类型,显示指向
ls -li original.txt symlink.txt
# 2097153 lrwxrwxrwx 1 user user 16 ... symlink.txt -> original.txt
# ↑ 不同 inode   ↑ l 类型

# 软链接可以跨文件系统,可以指向目录
ln -s /mnt/data /home/user/data-link

# 软链接的注意事项:
# - 原文件删除后,软链接变为"悬空链接"(dangling link)
# - 相对路径软链接:相对于链接文件所在目录,而非当前目录

# === 相关命令 ===
# 查看软链接的真实目标
readlink symlink.txt
# /path/to/original.txt

# 解析完整绝对路径(跟随所有软链接)
realpath symlink.txt

# 找出所有悬空软链接
find /path -xtype l  # -xtype l 匹配软链接,但目标不存在

# 统计一个文件的硬链接数
stat -c "%h" /bin/ls  # 通常为 1
特性 硬链接 软链接(符号链接)
inode 关系 共享同一 inode 独立 inode,存储路径字符串
跨文件系统 不支持 支持
指向目录 不支持(防止循环) 支持
原文件删除后 数据仍可访问 变为悬空链接,无法访问
ls -l 显示 与普通文件相同(链接数>1) l 类型,显示 -> 目标
常见用途 备份不占额外 inode 版本管理、路径别名

2.6 find 深度使用:强大的文件搜索

find 是 Linux 最强大的文件搜索工具,支持按名称、类型、大小、时间、权限等多种条件组合搜索,并能对每个找到的文件执行任意命令。

# === 按名称搜索 ===
find /home -name "*.log"           # 查找所有 .log 文件
find /etc -name "*.conf" -type f   # 只找普通文件
find / -name "passwd" 2>/dev/null  # 忽略权限错误
find . -iname "*.TXT"              # 忽略大小写

# === 按类型搜索 ===
find /var -type d -name "log*"     # 目录
find /dev -type b                  # 块设备
find /tmp -type l                  # 所有软链接
find /run -type s                  # Unix 套接字

# === 按大小搜索 ===
find /var/log -size +100M          # 大于 100MB 的文件
find /home -size -1k               # 小于 1KB 的文件(空文件附近)
find /tmp -size +10M -size -1G     # 10MB 到 1GB 之间
find / -empty                      # 空文件或空目录

# === 按时间搜索(单位:天)===
find /var/log -mtime -7            # 7天内修改过的文件
find /home -atime +30              # 30天以上未被访问
find /tmp -mtime +3 -type f        # 3天以上的临时文件
find /etc -newer /etc/passwd       # 比 passwd 更新的文件
find /var/log -mmin -60            # 60分钟内修改(-mmin,分钟)

# === 按权限搜索 ===
find / -perm 0777 -type f 2>/dev/null  # 权限为 777 的文件(安全风险!)
find / -perm -u+s -type f 2>/dev/null  # 有 SUID 位的文件
find / -perm -g+s -type f 2>/dev/null  # 有 SGID 位的文件
find /home -perm /o+w              # 其他用户可写的文件

# === 按所有者搜索 ===
find /home -user alice             # alice 拥有的文件
find /tmp -nouser                  # 没有对应用户的文件(孤立文件)
find / -group docker 2>/dev/null   # docker 组拥有的文件

# === -exec:对每个结果执行命令 ===
# {} 代表当前找到的文件;\ 是 -exec 的结束符
find /tmp -name "*.tmp" -mtime +7 -exec rm {} \;

# -exec 的更安全版本:-execdir(在文件所在目录执行)
find /home -name "*.bak" -execdir rm {} \;

# 将结果传给 xargs(效率更高,批量处理)
find /var/log -name "*.log" -mtime +30 | xargs rm -f
find /home -name "*.jpg" | xargs -I{} cp {} /backup/photos/

# === 组合条件 ===
# -a (AND,默认),-o (OR),! (NOT)
find /home -name "*.txt" -size +1M        # AND:txt 且大于 1MB
find /home \( -name "*.txt" -o -name "*.md" \)  # OR:txt 或 md
find /home ! -name "*.log"                # NOT:不是 .log

# === 深度控制 ===
find /etc -maxdepth 1 -name "*.conf"      # 只搜索一层深
find /usr -mindepth 2 -maxdepth 3         # 只搜索 2-3 层深

# === 实用组合示例 ===
# 找出过去24小时内被修改的文件,并显示它们
find /var/www -mtime -1 -type f -ls

# 找出大于 500MB 的文件,按大小排序
find / -type f -size +500M -printf "%s\t%p\n" 2>/dev/null | sort -rn | head -20

# 找出 SUID/SGID 文件(安全审计)
find / -type f \( -perm -4000 -o -perm -2000 \) -exec ls -la {} \; 2>/dev/null

# 统计目录下各类型文件数量
find /etc -type f | wc -l    # 文件数
find /etc -type d | wc -l    # 目录数

2.7 ls 深度:解读每一列的含义

$ ls -la /etc/passwd
-rw-r--r-- 1 root root 2847 Apr 20 14:22 /etc/passwd
^ ^^^^^^^   ^ ^^^^ ^^^^ ^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^
│ │         │ │    │    │    │             └── 文件名
│ │         │ │    │    │    └── 最后修改时间(mtime)
│ │         │ │    │    └── 文件大小(字节)
│ │         │ │    └── 所属组
│ │         │ └── 所有者
│ │         └── 硬链接数
│ └── 权限位(rwxrwxrwx:所有者/组/其他)
└── 文件类型(- d l c b p s)

# 常用 ls 选项
ls -l                  # 长格式
ls -la                 # 包含隐藏文件(.开头)
ls -lh                 # 人类可读大小(K, M, G)
ls -lS                 # 按文件大小排序
ls -lt                 # 按修改时间排序(最新在前)
ls -ltr                # 按修改时间反向排序(最旧在前)
ls -li                 # 显示 inode 号
ls -lR                 # 递归列出子目录
ls -ld /etc            # 只显示目录本身信息(不展开内容)
ls --color=auto        # 颜色区分文件类型(大多数发行版默认开启)

# 自定义颜色配置
# dircolors -p > ~/.dircolors
# 编辑 ~/.dircolors,然后在 ~/.bashrc 中加:
# eval "$(dircolors ~/.dircolors)"

# tree 命令(更直观的目录结构)
sudo apt install -y tree
tree /etc/nginx -L 2   # 显示 2 层深度
tree -sh               # 显示大小,人类可读
tree -d                # 只显示目录

2.8 文件系统类型:ext4 vs xfs vs 虚拟文件系统

ext4 vs xfs 对比

特性 ext4 XFS
设计目标 通用,向后兼容 ext2/ext3 高性能大文件,大规模并行 I/O
最大文件系统大小 1 EB 8 EB
最大单文件大小 16 TB 8 EB
日志(Journal) 完整日志支持 元数据日志(默认),可选数据日志
小文件性能 良好(内联小文件到 inode) 略低(专注大文件优化)
大文件性能 良好 优秀(延迟分配 + 条带化)
fsck 速度 慢(大文件系统上很慢)
在线缩减 不支持 不支持
默认发行版 Ubuntu, Debian RHEL, CentOS, Fedora
推荐场景 通用桌面/服务器,系统分区 数据库、视频流、大数据存储

虚拟文件系统

# === tmpfs(内存文件系统)===
# 数据存在 RAM 中,重启后消失,速度极快
# /tmp 通常挂载为 tmpfs
mount | grep tmpfs
# tmpfs on /tmp type tmpfs (rw,nosuid,nodev,size=8192M)

# 手动创建 tmpfs 挂载点
sudo mkdir /mnt/ramdisk
sudo mount -t tmpfs -o size=512M tmpfs /mnt/ramdisk
df -h /mnt/ramdisk

# === /proc 虚拟文件系统(procfs)===
# 内核在运行时动态生成的文件,不占磁盘空间
cat /proc/cpuinfo            # CPU 信息
cat /proc/meminfo            # 内存信息
cat /proc/version            # 内核版本
cat /proc/mounts             # 当前挂载点
cat /proc/net/dev            # 网络接口统计
cat /proc/loadavg            # 系统负载
cat /proc/$$/status          # 当前 Shell 进程状态($$ 是当前 PID)
ls /proc/1/                  # PID 1(init/systemd)的所有信息

# === /sys 虚拟文件系统(sysfs)===
# 内核设备模型的接口,可读写来控制硬件
cat /sys/class/net/eth0/speed          # 网卡速度
cat /sys/class/thermal/thermal_zone0/temp  # CPU 温度(单位 millidegrees)
cat /sys/block/sda/queue/scheduler    # 磁盘调度器
echo mq-deadline > /sys/block/sda/queue/scheduler  # 修改调度器(需 root)

# 查看当前所有挂载的文件系统类型
cat /proc/filesystems        # 内核支持的文件系统
mount -t ext4                # 只显示 ext4 类型的挂载
findmnt -t ext4,xfs          # findmnt 过滤特定类型

2.9 du 与 df:磁盘使用分析

df(disk free)报告文件系统级别的磁盘使用情况,du(disk usage)报告目录或文件占用的实际空间。两者配合使用可以快速定位磁盘空间问题。

# === df 命令 ===
df -h              # 人类可读格式(K/M/G)
df -H              # 使用 1000 而非 1024 计算
df -T              # 显示文件系统类型
df -i              # 显示 inode 使用情况(而非磁盘空间)
df -h /var         # 只显示 /var 所在文件系统

# df 输出解读:
# Filesystem      Size  Used Avail Use% Mounted on
# /dev/sda1        50G   32G   16G  67% /
# tmpfs           3.9G  1.2M  3.9G   1% /tmp

# === du 命令 ===
du -sh /var/log         # 整个 /var/log 目录的总大小
du -sh /*               # 根目录下各目录大小(快速定位大目录)
du -sh /home/*          # 每个用户的空间占用
du -h --max-depth=2 /   # 递归最多 2 层
du -h --max-depth=1 /var | sort -hr  # 排序后显示(最大的在前)

# 找出当前目录中最大的 10 个文件/目录
du -h . | sort -hr | head -10

# 找出系统中最大的文件(前20个)
find / -type f -printf "%s\t%p\n" 2>/dev/null | sort -rn | head -20 | \
    awk '{printf "%.1fMB\t%s\n", $1/1024/1024, $2}'

# === ncdu — 交互式磁盘使用分析器 ===
sudo apt install -y ncdu   # 安装
ncdu /                     # 扫描根目录(交互式,可以导航进入子目录)
ncdu /var                  # 扫描特定目录

# === 常见磁盘满问题排查流程 ===
# 1. 确认哪个分区满了
df -h

# 2. 定位大目录
du -h --max-depth=1 / 2>/dev/null | sort -hr | head -10

# 3. 进入大目录继续深挖
du -h --max-depth=1 /var | sort -hr

# 4. 找大文件
find /var/log -type f -size +100M -ls 2>/dev/null

# 5. 检查被删除但仍被进程持有的文件(常见于日志轮转)
lsof +L1 | grep deleted

# 6. 清理旧日志(journalctl)
journalctl --vacuum-size=200M    # 保留最近 200MB
journalctl --vacuum-time=7d      # 保留最近 7 天

磁盘满但 df 显示有空间? 两种常见原因:(1) inode 耗尽——用 df -i 检查,小文件过多会耗尽 inode;(2) 文件被删除但仍被进程持有——用 lsof +L1 查找,重启进程或系统即可释放。

章节总结: 你现在深刻理解了 Linux 文件系统的核心机制:FHS 目录规范告诉你每个目录的职责,inode 原理揭示了"文件名只是标签"的本质,硬链接与软链接的区别让你能合理使用这两种工具,find 的强大组合让任意文件搜索成为可能,du/df 帮你随时掌控磁盘状态。下一章将聚焦文件操作命令的精通使用。

  上一章
  ← 第1章:Shell 环境搭建


  下一章
  第3章:文件操作精通 →
本章评分
4.6  / 5  (88 评分)

💬 留言讨论