☰
Linux df -h 卡住不动?不重启定位并解决挂载点阻塞问题
2026/10/22 3:42:45 网站建设 项目流程

1. 一次诡异的运维现场:df -h 卡住不动了

那天下午,同事在群里甩了一张截图,终端里敲完df -h之后光标就一直停在那里,等了快两分钟没有任何输出,Ctrl+C 也退不出来,只能另开一个窗口。第一反应是“机器是不是挂了”,但top能正常刷、ssh也没断、业务进程照跑,唯独这个查磁盘的命令像被点了穴。

df -h这个命令,干运维的没人陌生,就是看各个挂载点的磁盘使用率,平时零点几秒就出结果。它一旦卡住,基本可以断定不是命令本身的问题,而是它在执行过程中被某个内核层面的阻塞操作给绊住了。这篇文章就把我这次排查的完整过程拆开讲清楚:为什么会卡、卡在哪一步、怎么在不重启机器的前提下定位并解决,以及后续怎么防止同类问题再发生。适合有一定 Linux 基础、日常需要维护服务器磁盘和挂载点的同学参考,新手也能跟着思路一步步看下来。

先说结论,方便赶时间的同学:df卡住绝大多数情况是有挂载点对应的后端存储失联了,比如网络存储(NFS、CIFS 之类)的对端不可达,或者本地块设备出现 I/O 挂起。df在统计文件系统信息时会去逐个访问挂载点,只要有一个挂载点“叫不应”,它就会一直等下去。下面把整个推理和操作过程完整还原。

2. 先搞清楚 df 到底在干什么

2.1 df 的执行流程拆解

很多人以为df就是读一下某个文件,其实它的工作比想象中重。它大致做了这么几件事:

  1. 读取/proc/self/mountinfo(或/etc/mtab)拿到当前所有挂载点列表;
  2. 对每一个挂载点,调用statfs()系统调用去获取该文件系统的块总数、可用块数、inode 等信息;
  3. 把结果格式化输出。

关键就在第 2 步。statfs()是一个会真正触达后端存储的操作。对于本地磁盘,这个调用几乎瞬间返回;但对于网络文件系统,它需要向远端发起请求。如果远端没有响应,这个系统调用就会一直阻塞,进程处于不可中断睡眠状态(D 状态),连kill -9都杀不掉。

所以df -h卡住,本质是某个挂载点的 statfs 调用回不来了。理解了这一点,排查方向就非常明确了:找出是哪个挂载点拖住了它。

2.2 为什么 Ctrl+C 也杀不掉

这里有个容易被忽略的细节。普通进程收到 SIGINT(Ctrl+C)会退出,但如果进程正卡在一个不可中断的系统调用里,信号是不会被处理的,要等系统调用返回后才会响应。这就是为什么按 Ctrl+C 没反应——不是终端死了,是那个进程根本没机会处理信号。

提示:遇到这种情况不要反复按 Ctrl+C,也不要急着kill -9,先按下面的方法定位是哪个挂载点的问题,否则强杀也可能无效,反而丢失现场信息。

2.3 和磁盘满不是一回事

顺便澄清一个常见误解:df卡住和“磁盘满了”是两码事。磁盘满的时候df照样秒出结果,只是使用率显示 100%。卡住是访问不到,不是写不进去。这两个问题的排查路径完全不同,别一上来就du找大文件,方向错了白费功夫。

3. 不重启机器定位卡住的挂载点

3.1 用 strace 抓住卡在哪一步

最直接的办法是strace。新开一个终端,找到卡住的df进程 PID,然后 attach 上去看它停在哪个系统调用:

# 找到卡住的 df 进程 ps aux | grep 'df -h' # 假设 PID 是 12345,attach 上去 strace -p 12345

如果输出停在类似这样的地方:

statfs("/data/nfs_share", {st_mode=S_IFDIR|0755, ...}) = ? <unfinished ...>

那答案就出来了——卡在/data/nfs_share这个挂载点的statfs上。<unfinished ...>就是它一直没返回的铁证。

如果机器上没装strace,也可以用/proc直接看进程状态:

cat /proc/12345/status | grep State # 输出 State: D (disk sleep) 就说明卡在 I/O 上了 cat /proc/12345/wchan # 会显示内核里正在等待的函数名,比如 rpc_wait_bit_killable

wchan这个文件很有用,它告诉你进程在内核里等什么。看到rpc相关的字样,基本就是网络文件系统的问题。

3.2 用 mount 和 findmnt 列出所有挂载点

定位到可疑挂载点后,把所有挂载点列出来对照一下:

findmnt -t nfs,nfs4,cifs # 或者 mount | grep -E 'nfs|cifs|fuse'

重点看那些网络类型的挂载点。本地 ext4、xfs 一般不会无故卡住,除非硬盘真的出问题了。网络存储才是重灾区。

3.3 逐个挂载点试探

如果strace不方便用,可以写个小脚本逐个挂载点去stat,看哪个会卡:

#!/bin/bash while read -r mp; do echo -n "checking $mp ... " timeout 3 stat -f "$mp" >/dev/null 2>&1 if [ $? -eq 124 ]; then echo "TIMEOUT (卡住了)" else echo "ok" fi done < <(awk '{print $2}' /proc/self/mountinfo)

这个脚本给每个挂载点 3 秒超时,超时的那个就是罪魁祸首。timeout命令在这里是关键,它保证脚本本身不会被拖死。

注意:timeout对处于 D 状态的进程同样可能无效,因为信号处理不了。所以这个脚本更适用于“响应慢但没完全死”的场景。真遇到完全卡死的,还是得靠strace或wchan。

4. 常见成因与对应处理方案

4.1 网络存储对端失联

这是最常见的原因。NFS、CIFS 这类网络文件系统,一旦服务端宕机、网络中断、或者防火墙规则变更导致端口不通,客户端上所有涉及该挂载点的操作都会挂起。

判断方法:

# 看服务端是否可达 ping <nfs_server_ip> # 看 NFS 端口是否通 nc -zv <nfs_server_ip> 2049 # 看当前 NFS 挂载状态 nfsstat -m

如果确认对端不可达,处理方式分两种:

  • 能恢复对端:把服务端拉起来或修好网络,挂载点会自动恢复,df也就正常了。
  • 对端短期恢复不了:需要强制卸载这个挂载点。

强制卸载网络文件系统:

umount -f /data/nfs_share # 如果 -f 也不行,用 -l 做懒卸载 umount -l /data/nfs_share

-l(lazy)的意思是先从命名空间里摘掉这个挂载点,等所有引用它的进程退出后再真正清理。这样df就不会再去访问它了。

4.2 本地磁盘 I/O 挂起

如果不是网络存储,那就要怀疑本地磁盘了。硬盘出现坏道、RAID 卡故障、或者存储控制器异常,都会导致 I/O 请求长时间不返回。

排查手段:

# 看内核日志有没有 I/O 错误 dmesg -T | tail -50 # 看磁盘健康状态(需要 smartctl) smartctl -a /dev/sdX # 看 I/O 等待情况 iostat -x 2 5

dmesg里如果出现I/O error、ata超时、medium error之类的字样,基本可以确定是硬件层面的问题。这种情况就不是软件能解决的了,得联系硬件维护,必要时做数据迁移。

4.3 挂载点被删除但未卸载

还有一种比较隐蔽的情况:挂载点目录被rmdir删掉了,但挂载关系还在。这时候df去访问这个“不存在的挂载点”就可能出问题。

# 找出挂载点目录已经不存在的挂载 findmnt -rno TARGET | while read -r mp; do [ -d "$mp" ] || echo "挂载点目录丢失: $mp" done

处理办法是重新创建目录再卸载,或者直接用umount -l懒卸载。

4.4 成因速查表

现象可能原因快速判断处理方式
df 卡住,wchan 显示 rpc网络存储失联ping/nc 对端恢复对端或 umount -f/-l
df 卡住,dmesg 有 I/O error本地磁盘故障smartctl、iostat硬件维护、数据迁移
df 卡住,挂载点目录消失目录被误删findmnt 对照重建目录后卸载
df 慢但能出结果网络存储响应慢测延迟优化网络或换存储

5. 实操复盘:一次完整的处理过程

5.1 现场还原

回到开头那次。机器上挂了一个网络存储到/data/share,用于存放备份文件。当天机房那边做网络调整,导致这台机器到存储服务端的链路中断了。

我按下面的顺序操作:

第一步,确认df进程状态:

ps -eo pid,stat,wchan:32,cmd | grep 'df -h' # 输出:12345 D rpc_wait_bit_killable df -h

D状态加上rpc_wait_bit_killable,直接锁定是网络文件系统的问题。

第二步,确认是哪个挂载点:

strace -p 12345 2>&1 | head -5 # statfs("/data/share", ... <unfinished ...>

第三步,验证对端:

ping -c 2 <storage_ip> # 100% packet loss

到这里结论已经很清楚了:/data/share对应的存储服务端不可达,df卡在它的statfs上。

5.2 处理动作

因为机房网络调整不是几分钟能恢复的,我选择先把这个挂载点摘掉,让df和其他依赖它的命令恢复正常:

umount -f /data/share # 报错 device is busy,因为有进程在用 # 改用懒卸载 umount -l /data/share

懒卸载执行后,再敲df -h,秒出结果,问题解决。那个卡住的df进程也随之退出了。

5.3 善后与验证

卸载之后做了几件事确认系统状态:

# 确认挂载点已摘除 findmnt | grep share # 确认没有残留的 D 状态进程 ps -eo pid,stat,cmd | awk '$2 ~ /D/' # 确认磁盘使用率正常 df -h

同时把/etc/fstab里这个挂载项检查了一遍,发现当初是用hard模式挂载的,没有加超时参数。这就是问题根源之一——hard模式下客户端会无限重试,永远不会放弃。

6. 从根上预防:挂载参数怎么配

6.1 网络存储挂载参数的选择

这次踩坑之后,我把所有网络存储挂载参数重新梳理了一遍。核心是别用无限等待的默认配置。

对于 NFS,推荐加上这些参数:

<server>:/path /data/share nfs defaults,soft,timeo=30,retrans=3,_netdev 0 0

逐个解释:

  • soft:软挂载。请求失败达到重试次数后返回错误,而不是无限等待。这是避免df卡死的关键。
  • timeo=30:单次请求超时 30 个 0.1 秒,也就是 3 秒。
  • retrans=3:重试 3 次。
  • _netdev:告诉系统等网络就绪后再挂载,避免开机时网络没起来导致挂载失败。

注意:soft模式有个副作用,如果网络只是短暂抖动,可能会返回 I/O 错误给应用。所以对数据一致性要求极高的场景,有人仍坚持用hard,但会配合intr参数允许中断。具体选哪种,取决于你的业务能接受哪种失败方式。我的经验是备份、日志这类场景用soft完全够用,数据库文件千万别放网络存储上。

6.2 本地磁盘的监控

本地磁盘虽然不容易卡df,但一旦出问题更严重。建议常态化监控:

# 定期检查 SMART 健康 smartctl -H /dev/sdX # 监控 I/O 等待 iostat -x 1

iostat里重点看%util和await。%util长期接近 100% 说明磁盘是瓶颈,await突然飙高往往预示硬件要出问题。

6.3 加一层超时保护

对于任何可能卡住的命令,养成用timeout包一层的习惯:

timeout 10 df -h || echo "df 超时,可能有挂载点异常"

这样即使真卡了,10 秒后也会自动放弃,不会把整个脚本或终端拖死。在自动化脚本里尤其重要。

7. 几个容易踩的坑和实操心得

7.1 别用 kill -9 硬刚

前面说过,D 状态的进程收不到信号,kill -9也没用。更糟的是,如果这个进程正持有某些资源,强杀可能引发其他问题。正确做法是先解决挂载点,进程自然就退出了。

7.2 umount 报 busy 怎么办

umount报device is busy是家常便饭。先用lsof或fuser找出谁在用:

lsof +D /data/share fuser -mv /data/share

找到进程后,能停的停掉,不能停的就用umount -l懒卸载。懒卸载不会立刻断开,但会从命名空间摘除,新命令不会再访问它。

7.3 排查顺序很重要

我总结的排查顺序是:先看进程状态(D 还是 R)→ 再看 wchan → 再 strace 定位挂载点 → 最后验证对端。这个顺序从粗到细,每一步都能缩小范围,避免上来就瞎试。

7.4 记录现场再动手

处理这类问题,动手前先把现场信息存下来:ps输出、strace结果、dmesg日志、mount列表。因为一旦卸载或重启,这些信息就没了,事后复盘或者提工单都用得上。我习惯存到一个临时文件里:

{ date ps -eo pid,stat,wchan:32,cmd cat /proc/self/mountinfo dmesg -T | tail -100 } > /tmp/df_hang_$(date +%s).log

7.5 常见问题速查

问题排查命令解决方向
df 卡住无输出ps + wchan定位 D 状态进程
不知道卡在哪个挂载点strace -p看 statfs 参数
umount 报 busylsof / fuser停进程或懒卸载
怀疑磁盘故障dmesg / smartctl硬件维护
想避免再次发生检查 fstab 参数改用 soft + timeo

这套流程走下来,df卡住这类问题基本都能在不重启的前提下解决。核心就一句话:df 卡住不是 df 的错,是它访问的某个挂载点回不来了,找到那个挂载点,问题就解决了一大半。剩下的就是根据对端能不能恢复,决定是等还是卸。我个人现在的习惯是,所有网络存储一律配soft加超时参数,宁可让应用收到一个错误,也不要让整个系统被一个失联的挂载点拖住。

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

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

立即咨询