西西软件园多重安全检测下载网站、值得信赖的软件下载站!
软件
软件
文章
搜索

首页 → 西西教程 → Linux → 进程卡在 D 状态杀不掉,我最后靠 mount 挂载参数救了它

进程卡在 D 状态杀不掉,我最后靠 mount 挂载参数救了它

相关软件相关文章发表评论 来源:本站整理时间:2026/11/11 8:32:11字体大小:A-A+

  • 类型:服务器区大小:17.2M语言:中文 评分:10.0
  • 标签:
立即下载

上周五晚上,客户半夜打电话来:网站接口大面积超时,登录页转圈十几秒,监控面板上 CPU 倒是不高,10% 都不到。我登上服务器第一眼看 top,进程列表里有个 nginx worker 挂着,状态列写着一个我不太想看到的字母D。

如果你正在处理进程卡死、磁盘 IO 阻塞方面的问题,或者需要一台配置合适的机器先把环境跑起来,可以先看看这几家的报价和技术说明:vmiss(服务器与主机,虚拟主机到专用服务器一站式);lightlayer(服务器与主机,独立物理服务器)

D 状态的全称是 uninterruptible sleep,中文一般翻译成「不可中断睡眠」。它的关键特征是:这个进程正在等一个几乎无法被中断的 I/O,信号递进去没反应,连 kill -9 都杀不掉。我当时的第一反应是「进程死锁了」,想重启服务了事,结果 systemctl restart 一直卡在那儿不返回,折腾了七八分钟才放弃事后证明这是我这晚犯的最贵的错。

Linux 服务器终端里进程卡在不可中断睡眠状态的操作台特写

第一步:先确认它到底在等什么

D 状态最容易被误判成死锁,但它跟死锁完全是两回事。死锁是进程等不到对方主动让步,而 D 状态是进程已经交给内核了,内核说「这个 I/O 我拿不到,你去等」,然后进程就进入一种叫 task_uninterruptible 的等待。它等的对象通常是四种:磁盘/NVMe 的物理 I/O、网络文件系统(NFS、CIFS)上的读写、块设备层的请求,以及页缓存回写。

确认方法很简单,先看进程栈:

cat /proc/<pid>/stack(需要 root,很多内核版本要开 CONFIG_STACKTRACE),或者用 cat /proc/<pid>/wchan 看它睡在哪个函数上。我的那个 worker 的 wchan 显示的是 nfs_wait_bit_killable这一眼就基本锁定了方向:NFS,不是本地盘。

同时看内核日志,dmesg 里的 task ... blocked for more than 120 seconds 那几条 hung task 记录能把阻塞点和栈打出来,这是最直接的证据。如果没开 hung task 检测,可以临时打开:

echo 120 > /proc/sys/kernel/hung_task_timeout_secs
echo 1 > /proc/sys/kernel/hung_task_panic

服务器机柜内数据盘存储阵列与硬盘特写,突出磁盘IO与存储链路

第二步:区分是「本地盘慢」还是「网络文件系统挂死」

这一步别跳过。我一开始怀疑的是云盘底层抖动,因为 iostat -x 1 里 %util 一直贴着 95 以上。但 iostat 看的是本地块设备,如果进程等的是 NFS,读写根本没落到本地盘上,%util 自然不会有什么异常变化。

真正的判据是这几条:

其一,看 /proc/<pid>/io 里的 read_bytes / write_bytes 这两个「真正落到块设备」的计数,如果它们几乎不动,而 rchar / wchar(系统调用层读写的字节数)在涨,说明数据卡在协议层,压根没到磁盘。

其二,用 mount | grep nfs 看挂载选项。我这台机器上的挂载是这样的:

10.0.0.8:/data /mnt/data nfs defaults,_netdev,vers=3 0 0

注意 soft 和 hard 这对选项的默认值。默认挂载是 hard,意思是服务端不响应就无限期重试、进程就一直 D 状态挂着好处是数据不丢,坏处是卡住的服务端会把客户端一起拖死。我出问题那台机器上,恰好有一批任务改成了 soft。

其三,挂载点本身的状态。stat /mnt/data 如果返回 stat: /mnt/data: Transport endpoint is not connected,说明服务端重启过、或者网络断了,客户端这边挂着的是已经失效的旧挂载。

NFS 网络文件系统挂载异常后的终端报错现场

第三步:为什么改个挂载参数就能救回来

根因找到了:那台 NFS 服务端在磁盘故障期间重启了几次,客户端的挂载没做 hard 保护也没配 intr/timeo,于是每一次读写都在无限重试。所有需要访问 /mnt/data 的 worker 一个接一个进 D 状态,进程表越堆越多,最后连 nginx 主进程想接管都接管不了因为 worker 的 fd 还卡在 NFS 的 RPC 调用上,主进程等它退出,主进程就等。

我的处理分两步:先止损,再修根。

止损是把挂载改成带超时的软挂载并允许中断,让请求能失败返回而不是无限等:

umount -l /mnt/data
mount -t nfs -o soft,timeo=100,retrans=2,vers=3 10.0.0.8:/data /mnt/data

这里 timeo=100 的单位是百分之一秒,也就是单次 RPC 最多等 1 秒,retrans=2 表示重试两次,累计 3 秒左右就放弃返回错误。umount -l 是 lazy umount,先从目录树摘掉挂载点,再等引用它的进程自己退出,这一步能避免直接 umount 卡住不动。

修根是给这类关键挂载换回 hard 加 intr 的组合,并且在 fstab 里写明 _netdev(保证网络起来之后才挂载)、nofail(挂载失败不阻塞开机)。另外加一条 systemd 的依赖,让依赖这个目录的服务在挂载就绪后再启动:

[Unit]
RequiresMountsFor=/mnt/data
After=remote-fs.target

最后别忘了 NFS 服务端那边也要有 rpc.statd、hard_mount 的一致配置,两端参数不对称是这类故障最常见的根因。

工程师在桌前排查进程阻塞问题的场景

复盘:这类问题我下次会先做的三件事

第一,kill -9 对 D 状态无效,别在重启服务上浪费时间,先 wchan + dmesg 定位等待源。

第二,挂载参数要当成配置来管。生产环境的 mount 选项应该是 review 过的,而不是随手一个 defaults。soft 挂载看似"更安全",实际上是把数据一致性的风险从服务端转移到了业务代码里,业务代码没做重试和降级就会直接把错误抛给用户。

第三,监控里一定要加 hung_task 和 NFS 客户端错误这两项。D 状态进程数量、NFS 重传计数,这俩指标在故障扩大之前就能报警。我这次是被客户电话叫醒的,其实 nfsstat -c 里的重传计数早就在涨了,只是没人看。

如果你的机器上已经出现类似的 D 状态进程堆积,先别急着重启。进程删不掉往往不是进程的问题,是它下面那块存储的问题,顺着 wchan 往下查一层,通常就能找到真正该修的地方。

进程状态流转示意:运行态、不可中断睡眠、IO等待

    相关评论

    阅读本文后您有什么感想? 已有人给出评价!

    • 8 喜欢喜欢
    • 3 顶顶
    • 1 难过难过
    • 5 囧囧
    • 3 围观围观
    • 2 无聊无聊

    发表评论

    昵称:
    表情: 高兴 可 汗 我不要 害羞 好 下下下 送花 屎 亲亲