第 5 章 · 06 磁盘容量:`df`、`du`


文档摘要

第 5 章 · 06 磁盘容量: 、 本节摘要:权限讲完了,本章最后两节把视角从「逻辑文件树」抬到「物理磁盘」。本节讲两个高频却常被混用的命令: (disk free)和 (disk usage)。 看的是「分区」级别——每个挂载点的总容量、已用、可用、使用率; 看的是「目录」级别——某个目录下各项占用了多少空间。两者维度完全不同: 回答「这个分区还剩多少」, 回答「这个目录吃了多少」。本节还要破解一个经典谜题——「我删了个 10G 大文件,为什么 显示空间没释放?」 答案藏在进程还持有该文件的 fd(文件描述符)里,根因是 Linux 的 inode 引用计数机制。理解了 / 的差别,你才算真正会诊断磁盘问题。 内容来源:原项目「Linux 命令大全」 与 ,精选并套用体系化模板。

第 5 章 · 06 磁盘容量:dfdu

本节摘要:权限讲完了,本章最后两节把视角从「逻辑文件树」抬到「物理磁盘」。本节讲两个高频却常被混用的命令:df(disk free)和 du(disk usage)。df 看的是「分区」级别——每个挂载点的总容量、已用、可用、使用率;du 看的是「目录」级别——某个目录下各项占用了多少空间。两者维度完全不同:df 回答「这个分区还剩多少」,du 回答「这个目录吃了多少」。本节还要破解一个经典谜题——「我删了个 10G 大文件,为什么 df 显示空间没释放?」 答案藏在进程还持有该文件的 fd(文件描述符)里,根因是 Linux 的 inode 引用计数机制。理解了 df/du 的差别,你才算真正会诊断磁盘问题。

内容来源:原项目「Linux 命令大全」command/df.mdcommand/du.md,精选并套用体系化模板。

学习目标

阅读完本节,你应当能够:

  1. df -h 查看每个挂载点的容量、使用量、可用量、使用率。
  2. du -sh 目录/* 查看某目录下各项的占用,用 du -sh . 查看总占用。
  3. 说清 dfdu 的维度差别:前者是「分区级」,后者是「目录级」。
  4. du--max-depth-d 限制递归深度,用 sort -rh 找出最大占用项。
  5. 解释「删大文件空间没释放」的根因:进程还持有该文件的 fd,inode 引用计数未归零。
  6. lsof | grep deleted 找出被进程持有的已删除文件,并正确释放空间。

一、设计动机:两种「看磁盘」的视角

设想两个常见的运维问题:

  • 问题 A:「这个服务器根分区快满了,我该怎么办?」——你要先知道每个分区用了多少、剩多少。
  • 问题 B:「/var/log 不知道被什么吃掉了 50G,怎么找出来?」——你要知道某个目录下各项各占多少。

这两个问题维度不同,需要两个工具:

  • df(disk free):看「分区」(文件系统)。每个挂载点一行,告诉你总量、已用、可用、使用率。回答「这个分区还剩多少」
  • du(disk usage):看「目录」。递归统计某目录下每个子项的占用。回答「这个目录吃了多少,被谁吃的」

关键概念:df 是「分区视角」,du 是「目录视角」。混用会导致诊断方向错——比如根分区满了,你去 du -sh /home/*,结果发现问题其实在 /var/log(不同分区或不同目录)。df 定位是哪个分区满了,再在那个分区里 du 找元凶,这是标准流程。

为什么两者有时给出的数字不一致

dfdu 统计的「已用空间」有时对不上,原因是:

  1. df 统计的是整个文件系统(分区)的元数据,包括 inode 表、超级块 reserved blocks(默认 5% 留给 root)等。
  2. du 只统计「可见文件」的占用,看不到文件系统元数据,也看不到被进程持有但已删除的文件。
  3. 被进程持有的已删除文件:du 看不见(文件已从目录树移除),但 df 还能看到(inode 没释放,空间还占着)。

这就是「删大文件空间没释放」的根因,详见下文踩坑。

二、高频组合与实战

1. df -h:人类可读的分区总览

$ df -h Filesystem Size Used Avail Use% Mounted on /dev/sda1 50G 30G 18G 63% / /dev/sda2 200G 150G 40G 79% /home tmpfs 4.0G 1.2G 2.8G 30% /tmp

-h(human-readable)用 KB/MB/GB 显示,最常用。关键看 Use%——超过 80% 就该警惕,超过 90% 服务可能出问题。

💡 技巧:df -h 还有个变体 df -hT(-T 显示文件系统类型 ext4/xfs/...),诊断「这个分区是什么格式」时有用。只想看某个挂载点:df -h /var

2. du -sh:看某目录的总占用

$ du -sh /var/log 3.2G /var/log $ du -sh . 8.5G .

-s(summary)只显示总和,不递归列出子项。-h 人类可读。这是回答「这个目录总共多大」的最快方式

3. du -sh *:列出当前目录下各项

$ cd /var && du -sh * 4.0K backups 2.1G cache 12K local 3.0K lock 820M log 1.2G lib

du -sh * 列出当前目录下每个子项的占用,这是定位「谁吃了空间」的第一步

4. 找出最大的几个目录(管道组合)

# 找 /var 下最大的 10 个子目录 du -sh /var/* | sort -rh | head -10 # └ 按大小降序 └ 取前 10

sort -rh(-r 降序、-h 识别 K/M/G 后缀)是配合 du 的经典组合。du + sort -rh + head」是磁盘排查的黄金三连

5. 限制递归深度

du -h --max-depth=1 /home # 只看 /home 的直接子目录, 不再深挖 du -h -d 2 /home # 看到第二层(简写 -d N, GNU 支持)

--max-depth=N(或 -d N)限制深度,避免 du 把整个目录树全打出来。排查时从浅到深:先 --max-depth=1 看大方向,锁定某个子目录后再深入。

6. 排除某些目录

du -sh --exclude=/proc --exclude=/sys / # 统计根目录, 排除虚拟文件系统

/proc/sys/dev 是内核虚拟文件系统,统计它们无意义(还可能很慢),排查根目录时务必排除。

7. 找出某用户或某类型的文件占用

# 找出所有超过 100M 的文件 sudo find / -type f -size +100M 2>/dev/null | head -20 # 找出 7 天内修改过的大文件(最近增长的元凶) sudo find /var -type f -size +50M -mtime -7 2>/dev/null # 按属主统计占用(看哪个用户最占空间) sudo find /home -type f -printf '%u %s\n' | awk '{sum[$1]+=$2} END {for(u in sum) printf "%s: %.1fG\n", u, sum[u]/1024/1024/1024}'

这三个是「磁盘侦探」的进阶武器——当 du 给出的目录粒度不够细时,用 find 按大小、时间、属主等条件精确定位元凶。find -size + du 是磁盘排查的「显微镜」组合

💡 技巧:排查「磁盘莫名满了」的标准三步:(1) df -h 确认是哪个分区满;(2) sudo du -shx /* 2>/dev/null | sort -rh | head 找根目录下最大的一级;(3) 进入那个目录,du -sh * | sort -rh | head 层层下钻,直到找到元凶文件。整个过程就是「df 定位分区 → du 逐层下钻」。

8. 监控磁盘使用率(运维场景)

生产环境要持续监控磁盘,常用:

# 简单告警: 根分区使用率超过 80% 就发警告 df -h / | awk 'NR==2 && int($5) > 80 {print "WARNING: root partition " $5 " used"}' # 配合 cron 定时检查 # crontab -e # */10 * * * * df -h | awk 'int($5)>80 {print "DISK FULL: "$0}' | mail -s "Disk Alert" admin@host

这是把 df 从「手动查看」升级成「自动监控」的标准模式——df + awk 取使用率 + cron 定时 + 邮件/告警。理解 df 的输出格式(第 5 列是使用率),才能写出这类监控脚本。

三、踩坑与排错

坑 1:删了大文件,df 显示空间没释放

这是最经典的磁盘「灵异事件」:

$ rm /var/log/huge.log # 删了一个 50G 大文件 $ df -h /var /dev/sda2 200G 150G 40G 79% /var # 怎么还是 150G 已用?

根因:有进程(比如某个 tail -f 或正在运行的日志服务)还持有该文件的 fd。Linux 的文件删除只是把目录项移除,真正的 inode 和数据块只有在「所有持有它的 fd 都关闭后」才释放(inode 引用计数归零)。所以文件「看不见了」(目录里没了),但空间还占着。

排查:

$ sudo lsof | grep huge.log # 或更通用地: 找所有"已删除但仍被持有"的文件 $ sudo lsof +L1 | head COMMAND PID USER FD ... NLINK SIZE/OFF NODE NAME app 1234 app 3w ... 0 50G ... /var/log/huge.log (deleted)

lsof +L1 列出「链接数为 0(已删除)但仍被打开」的文件。看 PID 是谁还持有,然后重启那个进程(或让它关闭 fd),空间就释放了。

关键概念:Linux 删文件不是「立即释放空间」,而是「等所有 fd 关闭后才释放」。这就是为什么删大文件前最好先 truncate> file(清空内容而不是删文件),避免日志进程还在写。

💡 技巧:安全清空正在被写入的大日志:> /var/log/huge.log(用重定向清空内容)。这样文件 inode 不变,进程持有的 fd 仍有效,但内容被清空,空间立即释放。比 rm 安全得多。

坑 2:dudf 数字对不上

如前所述,du 统计可见文件,df 统计整个文件系统。常见差异来源:

  • 被进程持有的已删除文件(df 算,du 不算)
  • 文件系统元数据、reserved blocks(df 算,du 不算)
  • 挂载点下挂了其他分区(du 会跨分区累加,df 只看本分区)

别指望两者数字完全一致,以 df 为准(它才是「分区真实占用」)。

坑 3:du 统计 /proc/sys 卡住或报错

$ du -sh / du: cannot access '/proc/1234/...': Permission denied ... (一堆报错, 还很慢)

/proc/sys/dev 是虚拟文件系统,du 统计它们无意义且容易出错。排查根目录务必加 --exclude:

sudo du -sh --exclude=/proc --exclude=/sys --exclude=/dev --exclude=/run /

或者用 --one-file-system(-x)只统计当前文件系统,不跨挂载点:

sudo du -shx /

坑 4:看不懂 Use% 超过 100%

某些情况 df 会显示使用率略高于预期,根因是 reserved blocks——ext4 默认预留 5% 给 root,普通用户看到「满了」但 root 还能写。这是设计,不是 bug,保证磁盘满时 root 还能登录救火。如果你看到 df 显示 95% 但 du 加起来对不上,差额可能就是 reserved blocks。

坑 5:du -h 排序错乱

du -sh /var/* | sort -rh # 对! du -sh /var/* | sort -r # 错! 1.2K 会排在 900M 前面(字典序)

sort 默认按字典序,1.2K900M 大(因为 1 > 9 在字典序里错,但 1.2 vs 900 字典序是 1 < 9...其实 sort 不认 K/M/G)。必须加 -h 让 sort 识别后缀:sort -rh

坑 6:稀疏文件让 du 显示异常

$ ls -l disk.img -rw-r--r-- 1 user user 10737418240 ... disk.img # 显示 10G $ du -sh disk.img 0 disk.img # du 说 0?! $ du -sh --apparent-size disk.img 10G disk.img

稀疏文件(sparse file)是只分配了「实际写入」那部分磁盘块的文件,ls -l 显示的是「逻辑大小」(声明的 10G),du -sh 显示的是「实际占用」(可能只有几 M)。这类文件常见于虚拟机磁盘镜像(.img、qcow2)。看到 lsdu 数字差几个数量级,先想到稀疏文件du --apparent-size 显示逻辑大小,du(不带该选项)显示实际占用。

⚠️ 注意:虚拟机/容器场景下,df 看的是宿主机分区占用,du 看的是镜像文件实际占用——两者差别巨大。别把容器内 df 的数字当成宿主机消耗。

坑 7:文件系统保留空间(reserved blocks)造成的「消失的 5%」

$ df -h /home /dev/sda2 200G 180G 20G 90% /home # 用了 180G, 剩 20G # 但 200 - 180 = 20G, 看起来对啊 # 实际上: reserved 5% = 10G 给 root, 普通用户能用的其实只有 10G

ext4 默认预留 5% 给 root,普通用户看到的「可用空间」比理论值小。这在 TB 级硬盘上尤其浪费——5% 可能是几十 G。调整保留比例:

sudo tune2fs -m 1 /dev/sda2 # 把保留比例从 5% 调到 1%(数据盘可以更低)

根分区不建议调低——保留空间是为了磁盘满时 root 还能登录救火,调太低会失去这个保险。这是 ext4 特有的机制,xfs 没有这个设计。

本节要点回顾

  1. df 是「分区视角」,du 是「目录视角」——先 df 定位是哪个分区满了,再在那个分区里 du 找元凶。
  2. df -h 看每个挂载点的容量、使用率;Use% 超过 80% 要警惕。
  3. du -sh 目录 看某目录总占用;du -sh * 列子项;du -sh * | sort -rh | head 是找元凶的黄金三连。
  4. --max-depth=N(或 -d N)限制递归深度,排查从浅到深。
  5. **「删大文件空间没释放」**根因:进程还持有该文件的 fd,inode 引用计数未归零;用 lsof +L1 找元凶,重启进程释放。
  6. 安全清空正在被写入的大日志用 > file(重定向清空),不要 rm——文件 inode 不变,进程 fd 仍有效,空间立即释放。
  7. du 统计根目录要排除 /proc//sys//dev,或用 -x(--one-file-system)只统计本文件系统。
  8. du -h | sort 必须加 -h(sort -rh),否则按字典序排,1.2K 会错位。

最后一节回到 Linux 文件系统设计的核心:挂载模型mount 把一个设备(磁盘分区、U 盘、ISO)接到文件树的某个目录节点,从而实现「一切皆文件树」。我们还要讲清「为什么必须先 umount 再拔 U 盘」——缓冲未落盘,直接拔可能丢数据。


发布者: 作者: 灏天文库 转发
评论区 (0)
U