df 数的是整个文件系统那一本账,du 是一个文件一个文件数出来的。两个数对不上,多数不是谁算错,是口径不同。
一、先给结论:整盘看 df,某个目录看 du
df 只对“文件系统”这一个对象说话:不带参数时列出所有已挂载的文件系统,带文件参数时只报告该文件所在的那个文件系统 [1]。du 统计的是一组文件(目录递归)实际占用的块数 [4]。所以“盘还够不够”看 df,“哪个目录大”看 du,两个数不能直接对账。
df -k . # 整个文件系统的三本账:Total / Used / Available
du -sk . # 只数它走到的文件:实测小目录 32 KB
du -x -sk . # 加 -x:只算当前这一个文件系统
实测(WSL2 Ubuntu 24.04.4、coreutils 9.4、ext4 根分区):df -k / 报 Used 8,492,304 KB,du -x -sk / 报 8,490,100 KB,差约 2.2 MB —— 那块是文件系统自身的元数据(块位图、inode 表、日志、组描述符),df 从文件系统统计里读,du 只能数它走到的目录和文件 [1][4]。
二、df 的三列各有口径:Total 不含元数据,Available 不含保留块
df -k /
tune2fs -l /dev/sdd | grep -iE "Block count|Reserved block count|Reserved blocks uid"
同一台机器上 df -k / 报 Total 1,055,762,868 KB、Used 8,492,304 KB、Available 993,567,092 KB;tune2fs -l 报超级块里 Block count 268435456(块大小 4 KB)、Reserved block count 13421772、Reserved blocks uid 0 [5]。两处口径要对上:
所以“df 说还剩 950 GB,可 du 加起来只有 8 GB”是正常的:df 是整盘口径,du 是它走到的那棵树。
三、五处最容易对不上的地方(均在 WSL 实测)
mkdir -p /tmp/du_df && echo hi > /tmp/du_df/a && ln /tmp/du_df/a /tmp/du_df/b
du -sk /tmp/du_df # 12:同一个 inode 只数一次
du -sk --count-links /tmp/du_df # 20:同一个 inode 数两次
du /tmp/du_df/a /tmp/du_df/b # 只打印 a;参数反过来写就只打印 b
truncate -s 10M /tmp/du_df/sparse.bin
du -sk /tmp/du_df/sparse.bin # 0:按实际占用的块算
du --apparent-size -sk /tmp/du_df/sparse.bin # 10240:按字节数算
1. 硬链接:du 默认同一个 inode 只数一次,--count-links(-l)才数两次 [3][4];参数顺序决定哪个名字被打印,实测 du a b 只打 a、du b a 只打 b。df 是整盘统计,没有这个问题。 2. 稀疏文件:du 默认报“实际占用的块”,--apparent-size 报字节数 [3][4]。实测 3000 字节的普通文件:du 报 4(一个 4 KB 块)、du -B 512 报 8、du --apparent-size 报 3;10 MB 的稀疏文件 du 报 0、--apparent-size 报 10240。 3. 已删除但仍被进程打开的文件:df 仍把它算进 Used,du 和 ls 都看不见它——目录条目已摘掉,块还没释放。实测 tmpfs:删除前后 df -k 的 Used 都是 20480 KB,du -sk 从 20480 掉到 0;lsof +L1 能列出它,readlink /proc/<pid>/fd/3 显示 (deleted)。这一条是实测观察,df 手册只说 df 读文件系统统计,没明写这个场景 [1]。 4. 挂载点:du 默认会跨挂载点,-x 才只算当前文件系统 [4]。实测在目录下挂 tmpfs 并写 1 MB:du -sk 报 1032,du -sk -x 报 8。另外 df 默认会隐藏重复的 bind mount 和伪设备条目,只保留挂载点名最短的那条 [2]。 5. 权限:df 只要路径能走通就能出数(它不读文件内容),du 读不到目录内容会报错并退出 1,但仍打印它读到的部分。实测以 nobody 跑:df 正常出数、退出码 0;du 报 cannot read directory '...': Permission denied、打印 4(目录条目本身)、退出码 1 [3][1]。
四、块大小口径:默认 1024,POSIXLY_CORRECT 时是 512
du -sk dir # 默认按 1024 字节块
du -B 512 dir # 显式指定块大小:同一棵树 12 → 24
POSIXLY_CORRECT=1 du dir # 12 → 24
POSIXLY_CORRECT=1 df -P . # 1,055,762,868 → 2,111,525,736
GNU 版 df/du 默认用 1024 字节,POSIXLY_CORRECT 置起来就换成 512 [1][4][2];按 POSIX 口径(POSIXLY_CORRECT 置起来时)就是 512 字节 [1],所以同一个数字换台机器可能翻倍。非整数一律向上进位 [2]。脚本里要稳定数字就用 df -k、du -sk,别用 -h:实测 df -h 把同一块盘显示成 1007G / 8.1G / 948G。
五、常用写法与排错速查表
df -i # inode 用量(整盘口径:实测 122498 已用 / 67108864 总量)
du --inodes dir # 按目录树数 inode(GNU 扩展,手册单列一条)
df -k -t ext4 dir # 只看某类文件系统;没有匹配时不输出且退出码 1(实测)
du --max-depth=1 dir; du --total dir; du --exclude=cache dir
排错速查表(现象 / 原因 / 处理):
tune2fs -l 对超级块lsof +L1 或 readlink /proc/<pid>/fd/N/mnt)—— 加 -x--apparent-sizedf -k -t ext4 dir 退出码 1 —— 该路径不是 ext4(无输出即失败,实测)[1]回滚与撤销:df、du 都是只读命令,不改文件。测试文件写在 /tmp/du_df 和 /tmp/hl,跑完已删除、tmpfs 已 umount,没有安装任何包(lsof 是这台机器基线里本来就有的)。有副作用的只有 tune2fs -m <百分比>(改保留块),本文只用了只读的 tune2fs -l [5]。
四个常见问题:du -sk . 和 df 为什么差 2 MB(元数据);du a b 为什么只打印一个(同一个 inode 已数过);du -h 和 du -B 512 为什么数字不同(块大小口径);df -i 和 du --inodes 的区别(前者整盘、后者这棵树)。
状态:草稿待审。实测环境 WSL2 Ubuntu 24.04.4(kernel 6.6.114.1-microsoft-standard-WSL2)、coreutils 9.4、e2fsprogs、util-linux,日期 2026-10-27。已实测:df 三列口径与超级块对照、硬链接只数一次与参数顺序、稀疏文件与 --apparent-size、已删除仍被打开的文件(df 仍算、du 看不见)、挂载点与 -x、权限不足时的退出码、POSIXLY_CORRECT 的 512 字节口径、df -t ext4 与 df -i、du --inodes/--max-depth/--exclude/--total。仅文档核查、尚未实际执行:df 隐藏重复 bind mount 条目、BSD/macOS 与 busybox 的默认块大小、tune2fs -m 改保留块。相对现有教程的增量:把 df 的 Total/Used/Available 三列分别对上超级块口径,并给出硬链接参数顺序这条实测结论。选题来自源站 2025-03-26 列表条目「Linux 查看磁盘占用:du 与 df 的区别」;本轮该站被宝塔 WAF 拦截(403/502),正文完全按官方文档与实测重写,未参考其正文。
参考资料
- [1] df(1) – Linux manual page (man7.org)(df(1) 手册页)
- [2] df invocation – GNU Coreutils manual(GNU coreutils df 手册)
- [3] du invocation – GNU Coreutils manual(GNU coreutils du 手册)
- [4] du(1) – coreutils – Debian bookworm man page(Debian du(1))
- [5] tune2fs(8) – Linux manual page (man7.org)(tune2fs(8) 手册页)
文中主要命令已在 WSL Ubuntu(24.04)实测,个别条目(未装工具/安装类命令)仅核对官方文档,未实际执行。