Linux终端卡死排查指南:检测进程状态与D状态进程
刚上 Linux 服务器就碰到终端卡住那个体验我相信谁都忘不了。按 CtrlC 没反应敲命令不回显光标像被冻住了一样心里免不了咯噔一下。但其实只要你手里还能打开第二个终端窗口或者能从本地再登录一次 SSH这台机器大概率还有救。真正要做的是趁终端“不动”的时候赶紧搞清楚“背后哪个进程在搞事”。这篇内容就围绕“Linux 下终端不动如何检测进程状态”这件事来写。我会从场景判断开始逐步拆解排查用的命令、状态的含义、处置的思路最后给一个完整的现场实操记录。如果你是运维新人或者后端开发偶尔要上服务器查问题这篇应该能帮你省不少踩坑时间。1. 先搞清楚你的“终端不动”是哪一种1.1 终端假死和系统濒死是两个完全不同的故事很多人一遇到终端没反应第一反应就是“服务器要挂了”其实不一定。终端不动只是一个表象根因可能很轻也可能很重。我在某公司维护内部环境时遇到过不少次这种情况大体分三类第一类是“终端自己卡住了”。比如你连了一个跳板机的 SSH 会话客户端这边网络波动服务端还活着但连接已经断了。这时候你按任何键都没反应但换个窗口再连一次系统贼流畅。第二类是“Shell 卡住了”。比如你敲了一个tail -f看日志没退出或者管道里某个命令还在等输入shell 在帮你等一个永远不来的数据表现出来就是回车没反应、命令输不进去。这种情况新开终端登录完全没事ps 里能看到那个卡住的子进程。第三类是“真的系统负载太高了”。比如某进程把 CPU 吃满或者一大堆 D 状态进程卡在磁盘 I/O 上导致新进程创建缓慢、登录都费劲。这种情况你新开终端也会觉得黏黏的敲命令有延迟但通常还是能敲进去只是很慢。想清楚自己是哪一类直接决定你下一步是“处理业务进程”还是“检查网络链路”。1.2 检测进程状态之前先给“进程状态”补个底做检测之前我建议先在心里过一遍 Linux 进程的核心状态。因为后面你在 ps 和 top 里看到的每一列状态标识都会直接帮你判断“这进程是在干活、在睡觉、还是已经死了”。最关键的是这四个Rrunning正在跑或者说在 CPU 运行队列里排队。看到一堆 R 不用慌说明它们是在运算问题可能是 CPU 被打满。Ssleeping可中断睡眠这是最健康的状态进程在等某个事件比如等网络数据、等锁释放。Duninterruptible sleep不可中断睡眠通常意味着进程正在等磁盘 I/O 或者其他内核资源。这是排查“终端假死”时最值得注意的状态因为它既杀不掉、也 CtrlC 不掉。Zzombie僵尸进程。子进程已经退出了但父进程没有调用 wait() 回收它。僵尸不占 CPU但大量堆积同样代表异常。另外还有 Tstopped被暂停和 Iidle空闲内核线程新内核里常见平时见到的概率低一些。为什么要补这块基础因为接下来所有检测手段本质上就是在回答三个问题进程是什么状态、是不是它搞的鬼、能不能收拾它。2. 另起一个终端用三个基础命令快速定位2.1 第一个落点ps 的“高级人肉搜索”用法终端虽然不动但我强烈建议你打开第二个窗口去做检测。登录上之后第一件事不是去看 GUI 面板之类的花活而是先跑一个带上关键信息的 ps 命令。我日常最常用的姿势是这个ps -eo pid,ppid,stat,pcpu,pmem,comm,%mem,wchan:30,cmd --sort-pcpu | head -30解释一下我为什么这么写。-e是列出所有进程-o自定义输出列stat看状态pcpu和pmem看资源和内存占用cmd看完整命令行。最后的--sort-pcpu是按 CPU 使用率从高到低排序head -30只取前 30 行。这台机器上究竟是谁在搞事往往一眼就出来了。如果怀疑是某个服务出问题也可以直接按进程名过滤ps aux | grep 具体进程名ps aux是老的但非常好用的写法输出里第二列是 PID第三列是 CPU 占用第四列是内存占用第八列是状态。这里有个很实用的经验第一次排查就把现场数据留全后面分析才有依据。我会用ps -eo ... /tmp/ps_output_$(date %F_%H%M).txt把快照留存再做处置。特别是事后要复盘的时候有这份快照能省太多事。2.2 实时观察top 和 htop 怎么用才算专业ps 看的是快照要观察趋势、看状态变化还得上 top。登录进备用终端后直接跑top -d 1表示每 1 秒刷新一次。top 界面的第三行是 CPU 状态其中wa那一项如果很高比如超过 30%说明磁盘 I/O 快扛不住了大量进程在等待 I/O终端自然就卡了。第四五行是内存和 swap多留意 swap 用量是不是一直往上飙。进到 top 里之后有几个按键一定要记住。按P按 CPU 排序按M按内存排序。按c显示完整命令行。按x高亮排序的那一列方便盯重点。按k可以直接输入 PID 杀进程。但我不建议你直接在 top 里杀因为误操作的概率高还是退出去确认好了再杀更稳妥。如果你想盯着特定进程可以启动的时候指定 PIDtop -d 1 -p PID比如你从 ps 里看到一个可疑进程 PID 是 12345那就top -d 1 -p 12345只观察它一个界面干净不少。htop 是 top 的增强版颜色更友好支持树状展示。如果机器上装了就用 htop没装就 top效果差别不大关键是会看状态那一列。2.3 比 top 更懂 I/O 的冷门工具pidstat 和 iotop有些时候 CPU 看起来不高内存也正常但系统就是卡。这种八成是 I/O 问题。top 里的wa只能告诉你“I/O 忙”但不告诉你“哪个进程在忙”。这时候需要更细的工具。pidstat是 sysstat 包里的命令能按进程维度输出 CPU、内存、I/O 状态。我最常用的一个姿势是pidstat -d 1 5-d是查看 I/O 统计1 5是每秒输出一次连续五次。它会显示每个进程的读写速率和块数找那个“读或写特别大”的进程就行。iotop更直观类似 top 的界面按磁盘读写量排序进程iotop -o -P-o只显示有 I/O 操作的进程-P只看进程、不展开线程。如果 iotop 没装可以直接yum install iotop或者apt install iotop不过在紧急时刻我更建议快速用pidstat因为它大概率在 sysstat 包里很多环境默认就有。3. 深挖到底顺着状态字段揪出元凶3.1 遇到 D 状态进程别急着 kill先看它卡在哪检测进程状态这件事里最让人头疼的就是 D 状态。这状态下的进程既不能被 CtrlC 终止你kill -9它也纹丝不动因为它在内核里等待一个不可中断的事件比如磁盘读写、网络文件系统等待、设备驱动忙等。如果 ps 里看到大量 D 状态进程配合 top 里上涨的wa基本可以锁定是 I/O 问题。但问题是I/O 问题是哪块盘的哪个文件的我习惯按这个顺序排查先用iostat -x 1 5看各块磁盘的%util和await列。%util接近 100% 说明那块盘已经忙到几乎没有空隙了await大说明平均每个 I/O 请求等待时间很长。然后用cat /proc/PID/stack看进程在内核栈里的位置。这个文件需要 root 权限如果权限够的话能看到类似request_wait、blkdev_direct_IO之类的字样指向的就是阻塞点。很多情况下是某块磁盘故障、RAID 卡降级或者远程存储失联引起的。顺便看一下/proc/PID/status里的State行后面会带(disk sleep)之类的说明。这些细节一叠加原因就清晰了。还有一招是用/proc/PID/wchancat /proc/PID/wchan这个文件会显示进程当前阻塞在内核的哪个函数上。如果结果是0或者-说明进程没在内核里等如果有具体函数名比如wait_on_page_bit、do_task_defer之类的那就是在做文件页缓存或目录操作等待。D 状态最常见的触发点我已经踩过好几轮了列在这里供参考触发场景表现解决思路磁盘硬件故障大量 D 状态iostat 里有盘%util满载或直接掉线换盘、恢复 RAID、确认挂载NFS 网络存储失联所有读写在 NFS 上的进程进 D负载虚高检查网络、存储服务端必要时强制重新挂载swap 分页抖动内存不足疯狂换页进程卡在换入换出清理内存、加内存、调整 swappiness文件系统异常df能看到挂载点但实际读写卡死排查磁盘控制器、内核日志提示见到 D 状态先别盲目重启。如果是一堆 NFS 进程卡住重启机器并不会阻止它们再次卡住必须先恢复存储端或网络链路否则就是白忙一场。3.2 僵尸进程别慌但要顺着父进程找源头僵尸进程在 ps 里的样子很醒目STAT 列是ZCMD 列后面带着[defunct]。严格说僵尸进程本身不消耗 CPU、不占内存它只是一个残留在进程表里的“状态记录”。真正的问题不在僵尸本身而在它的父进程不去回收它。很多守护进程都会定期处理子进程退出事件但如果代码里没有合理的 waitpid子进程退一个就攒一个僵尸攒多了进程表被占满类似“PID 耗尽”的连锁问题就来了。排查僵尸进程的方法很简单ps -eo pid,ppid,stat,cmd | grep defunct记下每个僵尸进程的 PPID父进程 ID顺着找是谁没干好事。你多半会发现一个不太健康的父进程比如某个脚本的守护进程或者一个年久失修的常驻服务。处置僵尸进程正确路径是先处理父进程而不是对僵尸本身动手。因为僵尸在父进程还活着的时候根本杀不掉。如果父进程是什么明确的服务且可以重启就重启父进程如果是 init 进程现在很多系统是 systemd的子进程它应该会自动清理如果一直没清理说明系统存在更深的问题。这里有一个我常用的检查点ps -o pid,ppid,stat,command -p 父进程PID看看父进程本身的状态。如果父进程也僵了或者变成了 D 状态那问题就升级为“父进程挂了但没完全挂”这种可能需要重启相应服务。3.3 别只看进程本身系统的全局指标也要扫一眼进程状态不是孤立的终端卡不卡、进程是不是有问题其实和系统全局状态深度耦合。所以有几个基础命令我每次都会顺带跑一遍哪怕它们看起来“和进程无关”。第一个是uptime。它会输出最近 1 分钟、5 分钟、15 分钟的平均负载。如果 1 分钟负载远高于 15 分钟说明负载正在快速飙升如果 15 分钟也很高那就是已经高位运行一段时间了。第二个是free -h。看内存和 swap。swap 如果大量占用说明物理内存曾经吃紧进程的页被换到磁盘上了访问这些页的时候会明显变慢终端用户体验就是“敲一下卡两下”。第三个是dmesg -T | tail -50。看内核日志查有没有 OOMOut Of Memory杀进程的记录、硬盘 I/O error、文件系统重挂载之类的信息。OOM 会选进程杀掉并写入日志很多时候你怀疑某个进程不见了其实是被内核杀了。第四个是df -h和df -i。前者看磁盘空间后者看 inode 是否耗尽。文件系统满了不一定终端马上卡但如果某个进程正在往已满的盘上写日志它就会进入反复重试的状态表现可能就是终端卡顿、进程呈 D/S 状态交替。这四个命令不需要死记硬背我自己的执行顺序是uptime-ps-top-iostat-free-dmesg。到了 dmesg 这一步问题的根因通常已经暴露了。4. 检测完不是终点怎么恢复终端才是关键4.1 先救“现场”把终端与会话分离现在假设你已经通过备用终端定位到问题进程了但原来那个卡死的终端还挂着呢。怎么把它恢复回来分场景处理如果卡死的是本地终端比如直接在机房或者虚拟机控制台操作的可以按Ctrl Alt F2切换到另一个 TTY 登录然后找到原本那个 shell 进程。用ps -ef | grep bash里面能看到的登录时间找到对应的 PIDkill -9掉那个 shell 进程就行。切回原 TTY会看到终端已经退回了登录界面。这种方式简单粗暴但对本地终端很有效。如果卡死的是 SSH 会话处理方式不一样。你可以从备用终端里执行ps -ef | grep sshd找到你那个会话对应的 sshd 进程。看清楚 PID 之后把它 kill 掉原终端会立刻断开回到本地 shell。但严格说这已经是“弃车保帅”因为你丢掉了原终端里可能还在运行的进程的会话环境。如果担心影响进程可以先看原终端里有没有重要任务在跑有的话要评估是否直接杀会话。另外还有一个经典的挽救办法对 Shell 卡住很有效按一下Ctrl \也就是 SIGQUIT 信号它会强制把前台进程和 shell 都踢掉效果比Ctrl C更坚决。我见过很多同事一直按Ctrl C没反应却从没想过按Ctrl \实际上后者在这种场景下往往直接奏效。4.2 处置问题进程分清“可杀”和“该留”检测到问题进程之后处置方式不是无脑kill -9这里的分寸感很重要。如果进程只是 CPU 占用高但它是正常的业务进程比如执行一个分析任务只是今天数据量特别大导致计算吃紧那更合适的做法是给它降优先级renice -n 10 -p PIDrenice把优先级的 nice 值提高让它少占用 CPU 时间把资源让给更关键的进程。nice 值范围是 -20 到 19值越高优先级越低普通用户只能调高root 可以调低。如果进程确实是异常导致的比如某个脚本由于 bug 进入了死循环那就可以考虑终止。终止也有讲究默认信号是 SIGTERMkill PID它允许进程做一些清理动作。kill -9 PID是 SIGKILL直接由内核回收进程没有机会清理内部状态。我的习惯总是先kill PID等个几秒钟如果进程还在再用kill -9 PID兜底。只有一种情况可以直接上 -9那就是进程已经确认是 D 状态之外且无响应的情况比如一个卡死到除了被内核杀掉没有别的路的进程。但 D 状态本身你 -9 也没用只能等 I/O 恢复后自行结束或者重启系统才能清掉。4.3 系统“脱险”之后把证据和日志整理成文本每次成功恢复系统之后我做的第一件事不是开开心心收工而是把这次排查的现场记录整理成文档留下来。具体做法很简单mkdir -p /data/logs/incident_$(date %F) ps -eo pid,ppid,stat,pcpu,pmem,cmd --sort-pcpu /data/logs/incident_$(date %F)/ps_snapshot.txt top -b -n 3 -d 1 /data/logs/incident_$(date %F)/top_snapshot.txt dmesg -T /data/logs/incident_$(date %F)/dmesg_snapshot.txttop -b表示批处理模式适合把输出重定向到文件里-n 3表示采样三次这样能留下一段时间的趋势。这算是我个人的一个执念吧。因为终端假死这类问题有一个特点现场转瞬即逝。你当时不记录等恢复之后想复盘很多关键信息就找不回来了。比如 D 状态的堆栈、当时的负载曲线、dmesg 里的 OOM 日志这些东西过了时间点就不会再出现第二次。5. 一次完整的现场排查实录模拟案例拆解5.1 接到告警终端没反应第一个登录的同事已经被卡住为了把这些命令和方法串成一条线我模拟一次完整的排查过程。假设你在一台内部测试服务器上部署服务突然有人喊“这台机器终端卡住了”。你从跳板机 SSH 过去输入命令以后屏幕上确实出现了字符但回显有延迟大概需要十几秒才能看到结果。第一反应是 “这台机器还能救”。于是你开了第二个 SSH 连接打开排查流程。5.2 逐步执行从 uptime 到 dmesg 的完整链路第一步uptime 摸负载。uptime输出load average: 18.7, 11.2, 6.8重点看 1 分钟负载 18.7明显高于 15 分钟的 6.8说明负载是三分钟内骤然拉高的。再结合机器的 CPU 核数比如 8 核18.7 的平均负载意味着运行队列里排着大量进程。第二步ps 找高占用进程。ps -eo pid,ppid,stat,pcpu,pmem,comm,cmd --sort-pcpu | head -20输出里的前几名是一个数据采集程序的多个 worker 进程。STAT 列是 RCPU 占用在 120%~300% 之间徘徊。另外还看到几个 java 进程状态是 DCPU 不高但 cmd 里明显在做文件读写。第三步top 确认系统瓶颈。top -d 1top 界面的第三行里wa已经飙升到 95% 左右。虽然有很多 R 状态进程但明显系统重心在等待 I/O。CPU 是被 I/O 拖累得看起来“闲置”了实际上一堆进程在等数据。第四步iostat 定位是哪块盘。iostat -x 1 5发现/dev/vdb1的%util是 100%await到了 3000 毫秒以上的夸张程度。正常情况下一块机械盘的 await 在 20 毫秒以下SSD 会更低3000 毫秒说明这块盘已经几乎“转不动了”。第五步free 和 df 排除空间类问题。free -h df -hT df -i内存用了不少但 swap 不高磁盘空间也还有余量inode 没耗尽。所以问题基本锁定在磁盘硬件或文件系统层面。第六步dmesg 看内核日志。dmesg -T | tail -50日志里有一条blk_update_request: I/O error, dev vdb1以及连续的Buffer I/O error记录。到这里根因基本清楚了vdb1 这块磁盘出现了硬件级 I/O 错误导致进程对这块盘的读写全部卡住不少进程进入了 D 状态。5.3 处置与后续先隔离问题盘再恢复终端最后升级硬件事宜确认是磁盘问题后优先做的是隔离影响面而不是在系统里反复调试。如果机器上 vdb1 只是挂载了一块独立数据盘可以先停掉对它的主要读写服务再把应用切回本地盘或备用存储让 D 状态进程摆脱阻塞。操作确认好之后才轮到换磁盘和修复 RAID 这种真正解决根因的动作。在这类现场里我吃过一个亏启动top -n 3输出就算完事后来复盘时才发现忘了看dmesg导致重要线索在日志里被淹没。从那以后我把dmesg -T固定写进排查脚本量化流程确实是排除系统性盲区的好帮手。6. 常见问题速查与避坑清单6.1 排查问题速查表这个表我常年贴在笔记里每次排查照着过一遍基本不出大差错现象优先检查可能原因终端敲命令特别慢CPU 正常iostat -x、top的 wa 列磁盘 I/O 瓶颈、坏道、远程存储故障终端完全无响应新登录也慢ps找大量 D 状态、dmesgNFS 断连、磁盘掉线、内核 I/O 死锁CPU 使用率超高一堆 R 进程ps --sort-pcpu、top按 P死循环脚本、异常定时任务、挖矿进程内存剩余不多swap 持续上涨free -h、ps --sort-pmem内存泄漏、进程占用过高僵尸进程越来越多ps -efgrep defunct父进程不回收子进程需要处理父进程进程 kill 不掉状态是 Dcat /proc/PID/stack、dmesg内核态阻塞先恢复 I/O 再处理服务器运行正常但 SSH 时常断检查客户端网络、SSH 保活配置网络不稳定、KeepAlive 未开启6.2 我自己踩过的坑希望你别再踩第一个坑一上来就 kill -9。进程状态还没看清就把业务进程杀了结果发现 CPU 占了 80% 的是一个无害的缓存构建任务真正的元凶在 D 状态里躺着。先ps后kill顺序对了才不会误伤。第二个坑看到负载高就以为是 CPU 问题。其实负载高既可能是 CPU 密集也可能是 D 状态进程堆积。用top看一眼wa是多少再下结论省得瞎折腾。第三个坑忽略了 D 状态进程的后续影响。D 状态进程如果一直不退出会让系统负载只升不降还会让kill -9失效。有一次我就是因为太着急反复发 kill 命令反而让进程卡得更深最后只能等 I/O 恢复。第四个坑只盯 PID 不盯命令行。多个同服务进程长得差不多直接按 PID 杀很容易杀错实例。先用cat /proc/PID/cmdline或者ps -p PID -o cmd看清楚再动手绝不亏。第五个坑分析 D 状态只盯进程本身不查/proc/PID/stack和dmesg。其实 D 状态的根因绝大多数在 I/O 层进程只是个“受害者”你要顺着内核栈里的线索去找存储和文件系统的位置。第六个坑终端卡住了只知道等。其实大多数情况下都可以另开终端先下诊断哪怕系统再卡Ctrl Alt F2切换本地 TTY 基本都能让你进到一个还能用的 shell。先稳住入口再谈修复这是所有排查的前提。我现在遇到“终端不动”的第一反应已经不是慌了而是习惯性地抓起第二个终端窗口先看一眼负载再抓一把进程快照然后顺着 D 状态往下摸。这套方法看着平淡但实测下来稳不管是测试环境的小卡顿还是线上服务器的迟滞都能快速找到问题进程、给出阶段判断。最后再分享一个压箱底的小技巧把上面说的排查命令存成一个脚本比如check_load.sh登进任何一台让你“觉得不对”的机器就先跑一遍输出的结果直接保存成带时间戳的文件。你当场可能会觉得多写了一行命令但在现场混乱的时刻有这份记录比后面回看印象中的“大概当时是……”要靠谱得多。