☰
彻底搞懂Linux df命令:磁盘空间、挂载点与df/du差异解析
2026/9/30 8:03:55 网站建设 项目流程

先从一个很常见的场景说起:某天你的服务突然告警,写文件报错“No space left on device”,你赶紧执行df -h一看,发现根分区明明是满的,但du -sh /统计出来的总大小却比df显示的已用空间小很多,怎么也对应不上。如果你在 Linux 下混过几年,这个场景一定不陌生。很多人用了很久 df,却一直没搞懂它和 du 到底谁说了算,也没去深究“挂载点”“文件系统”这些词的真实含义,反正看到满了就清日志,清完还是满就开始抓瞎。

这篇文章就想把df命令彻底讲透。我会先从“df 到底在显示什么”这个底层逻辑讲起,再拆解参数和输出字段,然后结合挂载点深入分析du对不上的原因,最后给出几个真正能落地解决问题的排查案例。不管你是刚接触 Linux 的运维新人,还是被磁盘问题折磨过几次的开发,这篇文章都能让你下次再看到磁盘告警时,少走几条弯路。

1. df命令是干什么的:一条命令看透磁盘空间的底层逻辑

1.1 先弄明白:df到底在显示什么

df的全称是 disk free,直译就是“磁盘空闲”。很多人第一次敲df -h的时候,看着输出的表头发懵:Filesystem、Size、Used、Avail、Use%、Mounted on,每一列都认识,但它们到底在描述什么,脑子里其实是模糊的。我刚开始接触 Linux 时也一样,总觉得 df 显示的是一块硬盘的“整体容量”,直到后来才明白,这完全是个误区。

df 显示的对象并不是物理硬盘,而是“文件系统”。你可以把文件系统理解成一块已经格式化、挂载到某个目录下的存储空间。它可能是一整个分区,也可能是 LVM 逻辑卷、磁盘阵列、网络存储,甚至是内存里的一块 tmpfs。每一个文件系统都有自己的总大小、已用空间、可用空间和使用率,df 做的就是把这些信息按挂载点逐一列出来。换句话说,df 统计的粒度是“挂载点”,而不是“硬盘”。

为什么要强调这一点?因为一个 Linux 系统上挂载点之间的关系是树状的:根目录/之下可以挂载很多个子目录,比如/home、/var、/boot。一旦某个子目录被挂载成独立文件系统,它就不再占用根文件系统的容量了。我第一次在服务器上执行df -h时,看到/和/home分别显示在不同的行,还以为 Linux 把同一块盘分成了两段,纠结了很久为什么/不是整块盘的大小。这个困惑现在回头看,其实就是没理解“挂载点会遮挡底层目录”这个核心概念。

1.2 df和du的经典误区:为什么两个命令统计结果不一样

先直接说结论:df统计的是文件系统层面的空间使用情况,它问的是“这个挂载点所在文件系统一共多大、用了多少”;du统计的是“目录树里所有文件实际占用的块大小”,它从目录项开始遍历,把每个文件的长度加总起来。

这两者的统计口径完全不同,所以数字对不上是正常现象,反而是常事。最常见的差异来源有几个:一是文件被删除但进程还持有文件句柄时,du已经看不见这个文件了,但它占用的块在文件系统里还留着,df的 Used 就比du大;二是/proc、/sys这类虚拟文件系统只存在于内存里,du遍历到它们时结果没有意义,而df却会展示它们的挂载点和容量;三是文件系统本身有元数据开销,比如 inode 表、日志区域,这些开销在du里不会体现,但df会算进总容量里。

我见过很多次有人在排查“磁盘空间去哪了”的时候,先用du -sh /统计,发现根目录总和远小于df的 Used,就开始怀疑服务器中毒或者日志被隐藏了。其实最可能的原因就是某个进程把一个大文件删了但没关句柄。这个时候正确的排查工具不是 du,而是lsof | grep deleted。类似这种经验,后面实战部分我会详细讲。

2. df命令参数速查与输出字段详解:一张表看懂全部细节

2.1 最常用的四个参数:-h、-T、-i、-x

参数不用全背,但有几个高频的必须滚瓜烂熟,否则排查问题时会觉得 df 不够用。

-h是 human-readable 的意思,把容量自动转换成 K、M、G 这样的可读单位。这个参数几乎所有人都用过,但有个小坑要提醒:-h使用的是 1024 进制,也就是 1G=1024M。如果你要和某些商业存储设备的标称容量(1000 进制)做对比,数字会差一点。想要严格的 1000 进制,可以用--si。

-T很实用,它会在输出里多显示一列Type,告诉每个文件系统的类型:ext4、xfs、btrfs、tmpfs、overlay 等。为什么这个参数重要?因为文件系统类型决定了你能用哪些维护工具。比如在 xfs 上跑resize2fs扩容,会直接报错,因为 xfs 要用xfs_growfs。我吃过好几次这个亏,所以现在凡是帮别人看磁盘,第一件事就是df -hT,类型、容量、挂载点一眼看全,不用猜。

-i显示的是 inode 使用情况,而不是磁盘块使用情况。这个参数在平时容易被忽略,但在小文件极多的场景下,它是救命稻草。有些目录里堆了几百万个缓存文件,每个文件只有几 KB,磁盘容量明明还剩很多,系统却开始报“磁盘满”。这时候你df -h看容量,一切正常;但df -i一看,inode 使用率已经 100% 了。inode 用尽意味着文件系统无法再创建任何新文件,哪怕还有几十 GB 空间。所以排查磁盘问题时,df -h和df -i必须一起看。

-x用于排除指定类型的文件系统。比如你只想看真实的磁盘文件系统,不关心 tmpfs、overlay、squashfs 这些临时或只读挂载,可以执行df -x tmpfs -x overlay -x squashfs。这在容器环境里特别有用,因为容器里/etc/hosts、/proc等一堆挂载点全是虚拟文件系统,默认输出会刷好几屏,加了排除参数后界面清爽很多。

2.2 输出字段逐列拆解:1K-blocks、Use%、Mounted on到底代表什么

以不带参数执行df为例,输出第一列是Filesystem,表示文件系统对应的块设备、远程路径或特殊标识,接着是1K-blocks、Used、Available、Use%、Mounted on。很多教程只会告诉你“这是容量”,但没解释为什么单位写的是“1K-blocks”,这个细节其实反映了 df 的工作原理。

1K-blocks并不是说每个文件系统固定按 1KB 块来计算,而是 df 把不同文件系统的容量统一换算成以 1024 字节为单位的块数,方便输出对比。比如一个 ext4 文件系统的块大小可能是 4KB,那它的总块数换算成 1K-blocks 时,就把每个 4K 块拆成 4 个 1K 块来显示,实际容量不变。这个换算逻辑会在POSIXLY_CORRECT环境变量设置后发生变化,默认情况下df(GNU 版本)反而会以 1024 字节为一个单位直接输出,所以有时候你会看到列名是1K-blocks,有时候是1024-blocks,本质是一样的。

Used表示已用块数,Available表示可用块数。这里有个细节:ext4 文件系统默认会为 root 用户保留 5% 的块,这部分既不算在 Available 里,普通用户也用不到。所以你会发现 Used + Available 不等于总容量,中间差的那 5% 就是 reserved blocks。在超大分区上,这 5% 可能多达几十 GB。如果确实需要释放这部分空间,可以用tune2fs -m 0 /dev/sdX调整为 0,但我不建议在生产环境这么做,因为保留空间在磁盘碎片化严重时是 root 进程恢复系统的最后保障。

Use%是已用容量占总容量的百分比,但注意它计算时用的是 Used / (Used + Available),而不是 Used / 总大小。因为 Available 不包括 reserved 部分,所以当 df 显示 100% 时,实际磁盘不一定是真的把每一块都写满了,只是普通用户能用的配额已经耗尽。Mounted on是挂载点,也是这一行数据的索引,后面实战判断“哪个分区满了”就看这一列。

2.3 冷门但实用的参数:--total、-l、-P 和 --sync

参数不一定都常用,但有几个在特定场景下非常顺手,属于一出手就能看出有经验的。

--total会在输出末尾追加一行汇总,把所有文件系统的容量和用量加在一起,适合一次性概括整台机器的磁盘状态。配合-h使用效果最好:df -h --total。不过要提醒,如果机器上挂了多个大容量备份盘,汇总值可能没有太多参考意义,因为磁盘不可能共享。

-l(小写 L)表示只看本地文件系统,把 NFS、FUSE 这类网络挂载排除在外。排查本地磁盘满的时候,用-l可以避免被远程存储的状态干扰。反过来,指定-t nfs则只看某个类型的文件系统,和-x正好是一对。

-P是 POSIX 兼容格式输出。默认 df 在文件系统名过长时会换行,导致脚本解析字段错位,-P强制单行输出,保证每行恰好 6 个字段。我自己写过好多解析 df 的脚本,一开始没加-P,偶尔出现一条换行,整段结果就乱了。后来规范统一:凡是脚本里用 df,必加-P。

--sync是在统计前先执行一次 sync 系统调用,把内存中的脏数据刷到磁盘,再读取容量信息。正常情况下不需要,但在极端的断电、宕机恢复场景下,文件系统元数据可能还没落盘,加上这个参数能拿到更接近真实的值。代价是等待时间会变长,所以我只在恢复环境里用它。

3. 挂载点与文件系统的边界问题:df结果背后的Linux存储架构

3.1 挂载点的“遮挡”效应:为什么df /看到的不是整块磁盘

如果你在服务器上执行df -h,会很自然地认为 / 分区就是整块硬盘的大小,其实不一定。物理硬盘可以划分出多个分区,每个分区格式化成文件系统后,再挂载到不同的目录。一个文件系统一旦挂载到某个目录,这个目录原本的内容就被“遮住”了,你在该目录下看到、写入的都是新挂载的文件系统内容。这就是挂载点的遮挡效应。

举个我实际遇到的例子:一台服务器的根分区只有 20G,家目录/home单独划分在另一块 500G 的盘上。系统跑着跑着,某个应用往/home下写数据,写满了 500G。按直觉判断,根分区应该不受影响才对,但结果系统直接卡死,因为应用把日志也写到了/var/log,而根分区只有 20G,早就撑爆了。我当时的反应就是先执行df -h,一眼看到/的 Use% 是 100%,再按挂载点逐个排查,才定位到日志文件。这个例子说明,df 的价值恰恰在于它能按挂载点把空间账目算清楚,告诉你问题到底出在“哪个目录所属的文件系统”上,而不是笼统地看整台机器。

理解了遮挡效应,也能解释一个新手经常犯的错:在某个挂载点上创建了一个目录,然后umount卸载文件系统,目录消失了,其实目录本身还在,只是它被“遮挡”的内容重新露了出来。同理,当你df查看一个子目录时,系统会向上层查找该目录所在挂载点对应的文件系统。比如执行df /var/log输出的是根分区而不是/var/log自己的挂载点(如果它没有单独挂载),这个查找逻辑是由内核 VFS 维护的挂载树决定的。

3.2 伪文件系统为何“空间为0”:/proc、/sys、tmpfs 的真实面目

在 df 的输出里,/proc这类挂载点往往显示容量是 0,使用率也是 0%。很多人第一次看到会觉得怪异:目录还能空间为 0?其实它们是“伪文件系统”,数据并不存储在磁盘块上,而是由内核在读取时动态生成。这类文件系统的存在意义是提供进程信息、内核参数、设备状态等接口,根本没有必要占用真实存储。

/proc是一个典型的 procfs,挂载类型通常显示为proc。它下面的每个数字目录都对应一个进程 ID,/proc/meminfo、/proc/loadavg等文件虽然可以被 cat 查看,但它们不是普通文件,而是内核数据的映射。你用du去统计 /proc 也不会得到有意义的体积。/sys是 sysfs,用同样的思路暴露设备、驱动、固件信息,同样空间为 0。

还有一类经常被忽略的是tmpfs,比如/dev/shm、/run,它们挂在内存上,容量默认是物理内存的一半。df 能看到 tmpfs 的大小和使用量,但要注意,这个“使用量”是动态变化的,里面的文件一旦删除,内存立即释放。在生产环境里,如果误把临时文件大量写到/dev/shm,会导致系统可用内存锐减甚至 OOM,而 df 上显示的使用率却不会像普通磁盘那样直观地体现风险。所以看到 df 里有 tmpfs 挂载时,我的习惯是顺带free -h看一眼内存,双端核对。

3.3 df与du对不上的真正原因:被删除文件、隐藏挂载点与只读文件系统

很多人在“磁盘空间神秘消失”的问题上栽过跟头,其实把 df 和 du 的差异原因吃透,就能少踩九成的坑。先说最常见的一种:文件被删除但进程仍然打开。Linux 下删除一个文件只是从目录项里移除链接,如果某个进程已经打开这个文件,它的 inode 还会继续保留,占用的数据块直到进程关闭句柄或进程退出才会释放。ps 里看到还在运行的进程,lsof | grep deleted就能列出所有这类文件。

第二种是隐藏挂载点。当你在某个目录下挂载了一个文件系统,但该目录同时还有大量原有文件没清理,du -sh /会把挂载点和被遮挡的旧文件一起统计进去,导致 du 统计结果大于实际可访问的内容。还有一种情况,程序的当前工作目录被删除后,进程仍然处于该目录中,此时 du 从根目录遍历看不到这个目录,但 df 的空间占用还在。

第三种情况是文件系统里存在“孤儿文件”或日志记录,比如 xfs 的日志区域、ext4 的保留 inode 表,这些元数据开销在 du 统计里完全不可见,但在 df 的 Used 里占有一席之地。单一文件系统上这类开销通常很小,但如果你创建了成百上千个小文件,inode 表会明显膨胀,df 和 du 的差异也会变大。

遇到 df 和 du 对不上的情况,我的排查顺序是:先确认是不是有 deleted 文件被进程占用,再检查是否存在隐藏挂载点,最后才考虑文件系统元数据开销。前两者的占比通常远大于第三者。

4. 实战:三分钟排查磁盘空间异常

4.1 场景一:df显示有空间,但系统提示磁盘已满

这个场景我在工作上遇到过不下十次。应用报错“No space left on device”,我马上执行df -h,显示的却是/还有 20% 可用,其他挂载点也正常。第一反应是怀疑应用误报,但日志文件确实写不进去。这时候就要想起 621 前面提到的 inode 耗尽了。

排查过程三步走:

# 1. 确认 inode 使用率 df -i # 2. 找出哪个目录下小文件数量惊人(统计单位:个) for dir in /var /tmp /home /opt; do echo "$dir: $(find $dir -xdev -type f 2>/dev/null | wc -l)" done # 3. 查看是否因为某个进程占用了已删除文件的句柄 lsof +L1

我遇到最夸张的一次,应用服务器上有个临时目录每小时生成几十万个 1KB 的 session 文件,没人写清理逻辑,一个星期就把 inode 耗尽了。df -h 完全正常,df -i 显示 100%,排障时df -i干净利落地锁定了方向。这种场景里的修复方法也很直接:删除无用小文件,再配置 logrotate 或写个定时任务清理临时目录,问题就能解决。但根本上的建议是,小文件量巨大的目录尽量单独挂一个分区,把 inode 压力和系统盘隔离。

4.2 场景二:df显示100%占用,怎么定位是哪个进程

df 显示某个挂载点 100% 后,最直接想知道的当然是“什么东西占满了”。一般思路有两种:如果目录结构清晰,用 du 从挂载点根目录逐层往下扫;但还有一种更讨巧的方式,直接看哪些进程打开了这个文件系统上的大文件,尤其是已经删除但未释放的。

先做大文件定位:

# 从根目录开始,限一层深度,找出容量最大的子目录 du -h --max-depth=1 /var 2>/dev/null | sort -hr | head -10 # 定位具体文件 find /var -xdev -type f -size +100M -exec ls -lh {} \; 2>/dev/null

如果 du 扫出来的结果远小于 df 显示的已用空间,基本可以断定是 deleted 文件占着坑:

lsof | grep deleted

lsof 会输出进程 PID、文件大小、文件路径。看到 size 列特别大且路径后带 “deleted” 字样的记录,直接kill -9 PID(或者更优雅地重启对应进程),空间立刻释放。有一次线上磁盘打满,业务紧急,我定位到是一个日志服务进程的日志文件被 logrotate 删除,但进程句柄没关,文件还在占空间。lsof 一查一个准,重启那个服务后 df 使用率马上降下来了。

4.3 场景三:用df + du + ncdu 三步定位大文件

如果只是日常清理磁盘,我的固定组合是 df + du + ncdu。

先用df -hT全局判断哪个文件系统快满了,缩小范围;再进入对应挂载点,用du -h --max-depth=1逐层排查哪个子目录膨胀;如果目录层级深、文件多,du 一层层敲太慢,直接装 ncdu,它是个终端交互工具,界面里可以看到每个目录的实时体积,能快速“钻”进最大的目录找大文件。

ncdu 的安装很简单,Debian/Ubuntu 用apt install ncdu,CentOS/RHEL 用yum install ncdu或dnf install ncdu。启动也很简单:

ncdu /var

然后就能用上下方向键浏览,回车进入目录,d键删除文件,q退出。对大文件、大目录的清理效率比 du + find 高一个量级。我平时给客户处理“磁盘满了”的工单,大部分流程都能在十分钟内走完,ncdu 功不可没。不过要提醒一句,ncdu 在 NFS 挂载上扫描会非常慢,而且它对目录的统计也是基于 du 的算法,如果存在隐藏挂载点或 deleted 文件,它同样看不到,这种场景还是要回到 lsof。

5. 进阶玩法:把df“用活”

5.1 定时监控磁盘空间并发送告警

运维监控平台里当然有磁盘告警功能,但如果你只是想给几台小服务器快速做一个告警脚本,没必要引入整套监控体系,一个 cron 任务加两行 shell 就够。

我常用的是一个简单脚本:

#!/bin/bash threshold=90 df -hP | tail -n +2 | while read fs size used avail use mount; do use=${use%\%} if [ "$use" -ge "$threshold" ]; then echo "Warning: $mount ($fs) usage is $use%" >> /var/log/disk-monitor.log fi done

这里有两个细节值得说:一是-hP,P保证单行输出,方便 while read 按字段读取;二是 use 变量去掉百分号后做数字比较。在 crontab 里挂个*/5 * * * * /usr/local/bin/check_disk.sh,每隔五分钟扫一次,超阈值就写日志。如果你希望有主动通知,可以把 echo 改成 curl 调企业微信或钉钉机器人的 webhook,或者直接用 mailx 发邮件。

5.2 用df辅助脚本判断挂载点的可写性

脚本运行起来经常要处理外部存储设备的挂载状态。比如有个备份盘挂在/mnt/backup,脚本执行前需要判断它是否成功挂载、是否可写。光看目录有没有-d /mnt/backup是不够的,因为目录存在不代表文件系统真的挂上了,可能是挂载失败后留下的空目录。

我自己的判断方式是:

if df -hP /mnt/backup | tail -n +2 | grep -q "/mnt/backup$"; then echo "backup mount ok" else echo "backup mount failed" exit 1 fi

这种判断方式比mountpoint /mnt/backup更直观,而且在容器里也适用,因为容器内可能没有 mountpoint 命令。更严格一点,还可以向挂载点写入一个临时文件再删除,测试真实读写权限。我写过很多备份脚本,这一层的校验帮我在凌晨的定时任务里避开了不少“挂载没生效但脚本照跑、数据全写到本地盘”的事故。

5.3 常见的alias优化与输出美化

很多人给 df 配 alias,最常见的写法是:

alias df='df -hT'

加了-T以后,每次执行都直接显示文件系统类型,省得排查问题再补参数。但也有一个副作用:某些场景下你想用原始输出格式,比如脚本解析时,alias 会带来干扰。所以我在交互式 shell 里用 alias,脚本里一律用完整的df -hP,不会依赖 alias 展开,这是个兼顾便利和稳妥的习惯。

如果你想看得更美观,也可以直接使用df -hT --total,末尾汇总对多磁盘机器很有用。还有一个比较好用的技巧:把df和watch结合起来实时监控,比如:

watch -n 2 'df -hT'

这个命令每两秒刷新一次,在做磁盘压力测试或者数据迁移时非常有用,可以肉眼观察使用率的变化。watch 本身是系统自带命令,几乎所有发行版都预装了,不用额外配置。

6. 常见问题速查表与避坑经验

6.1 高频问题速查表

问题现象可能原因排查命令处理方法
df 显示空间充足,但写文件报“磁盘满”inode 耗尽df -i删除大量小文件,或扩 inode
df 的 Used 比 du 统计结果大很多进程占用已删除文件lsof | grep deleted重启占用进程让句柄释放
df -h 有两行相同的挂载点路径磁盘分区重叠挂载mount | grep 路径清理异常挂载项,避免混淆
根分区显示使用率比实际文件总和大其他挂载点“遮挡”了根目录下的文件du -x /统计时加 -x 跳过其他挂载点
磁盘明明刚扩容,df 不显示新容量文件系统未在线扩容根据 FS 类型执行 resize 命令ext4 用 resize2fs,xfs 用 xfs_growfs

这张表里的每一行都是实际工作中反反复复出现的坑,尤其是第一行“inode 耗尽”和第四行“遮挡效应”,几乎每个 Linux 管理员早晚都会碰上一次。初学者可以先把表格存下来,遇到症状时对着排查,能省不少时间。

6.2 我的几个实际经验总结

做运维和写脚本这几年,df 命令给我最大的启发是:它看起来简单,但背后的“挂载点”“文件系统”“块”这些概念,才是 Linux 存储体系的骨架。如果你只记住参数,遇到怪问题依然会卡壳;但当你理解了 df 统计的是文件系统而不是目录、理解了挂载点的遮挡关系,很多问题的排查思路就自动通了。

最后再分享两个小技巧。一是在线上环境排查磁盘问题时,别急着删文件,先确认删掉的是不是被进程占用的文件,否则删了空间也不会释放。二是养成df -h和df -i一起看的习惯,把 inode 检查作为磁盘健康巡检的固定项,很多时候能提前发现隐患。我自己现在每台服务器的例行巡检里,都会把这两条命令的输出落盘保存,出了问题回查历史记录,比靠记忆猜要靠谱得多。

df 命令本身不难,难的是对各种边界情况有预期。希望这篇内容能帮你把这条命令真正用明白。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询