先从一个很常见的场景说起某天你的服务突然告警写文件报错“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 进制也就是 1G1024M。如果你要和某些商业存储设备的标称容量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环境变量设置后发生变化默认情况下dfGNU 版本反而会以 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-depth1 /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 deletedlsof 会输出进程 PID、文件大小、文件路径。看到 size 列特别大且路径后带 “deleted” 字样的记录直接kill -9 PID或者更优雅地重启对应进程空间立刻释放。有一次线上磁盘打满业务紧急我定位到是一个日志服务进程的日志文件被 logrotate 删除但进程句柄没关文件还在占空间。lsof 一查一个准重启那个服务后 df 使用率马上降下来了。4.3 场景三用df du ncdu 三步定位大文件如果只是日常清理磁盘我的固定组合是 df du ncdu。先用df -hT全局判断哪个文件系统快满了缩小范围再进入对应挂载点用du -h --max-depth1逐层排查哪个子目录膨胀如果目录层级深、文件多du 一层层敲太慢直接装 ncdu它是个终端交互工具界面里可以看到每个目录的实时体积能快速“钻”进最大的目录找大文件。ncdu 的安装很简单Debian/Ubuntu 用apt install ncduCentOS/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 threshold90 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这里有两个细节值得说一是-hPP保证单行输出方便 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 dfdf -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删除大量小文件或扩 inodedf 的 Used 比 du 统计结果大很多进程占用已删除文件lsof | grep deleted重启占用进程让句柄释放df -h 有两行相同的挂载点路径磁盘分区重叠挂载mount | grep 路径清理异常挂载项避免混淆根分区显示使用率比实际文件总和大其他挂载点“遮挡”了根目录下的文件du -x /统计时加 -x 跳过其他挂载点磁盘明明刚扩容df 不显示新容量文件系统未在线扩容根据 FS 类型执行 resize 命令ext4 用 resize2fsxfs 用 xfs_growfs这张表里的每一行都是实际工作中反反复复出现的坑尤其是第一行“inode 耗尽”和第四行“遮挡效应”几乎每个 Linux 管理员早晚都会碰上一次。初学者可以先把表格存下来遇到症状时对着排查能省不少时间。6.2 我的几个实际经验总结做运维和写脚本这几年df 命令给我最大的启发是它看起来简单但背后的“挂载点”“文件系统”“块”这些概念才是 Linux 存储体系的骨架。如果你只记住参数遇到怪问题依然会卡壳但当你理解了 df 统计的是文件系统而不是目录、理解了挂载点的遮挡关系很多问题的排查思路就自动通了。最后再分享两个小技巧。一是在线上环境排查磁盘问题时别急着删文件先确认删掉的是不是被进程占用的文件否则删了空间也不会释放。二是养成df -h和df -i一起看的习惯把 inode 检查作为磁盘健康巡检的固定项很多时候能提前发现隐患。我自己现在每台服务器的例行巡检里都会把这两条命令的输出落盘保存出了问题回查历史记录比靠记忆猜要靠谱得多。df 命令本身不难难的是对各种边界情况有预期。希望这篇内容能帮你把这条命令真正用明白。
阅读完成 · 觉得有帮助?