进程卡住先别急着 kill -9:用 strace、/proc 和信号把进程状态讲清楚
程序跑着跑着不动了,日志也不再滚动,第一反应往往是 kill -9 直接干掉重开。删掉一个进程很容易,但如果不知道它为什么卡住,下次还会在同一个地方卡住。先花两分钟看清楚进程在干什么,再决定怎么处理。
第一步:先看进程处在什么状态
ps aux | grep <进程名> 能看到进程的基本信息,重点看 STAT 这一列。R 是正在运行,S 是在等某个事件(比如等网络回包、等磁盘读写),D 是在等磁盘 I/O 完成,T 是被暂停了,Z 是僵尸进程。
S 和 D 都表示“在等”,但性质完全不同。S 状态的进程一般是正常的等待,收到数据就会继续;而 D 状态的进程卡在不可中断的 I/O 上,这时候就算发 kill -9 也杀不掉它,只能等磁盘 I/O 完成,或者解决底层的存储问题。先分清状态,再决定下一步。
如果想看得更细,/proc/<PID>/status 里有进程的完整状态,包括内存占用、打开的文件数、信号掩码等。cat /proc/<PID>/status | grep -E 'State|VmRSS|Threads' 一行命令就能拿到关键信息,不用装任何工具。
第二步:用 strace 看它到底在等什么
知道进程在等还不够,要知道它在等谁。strace 可以跟踪一个进程正在做的系统调用:
strace -p <PID>
如果输出一直在重复 read(3, ...) 且没有返回,说明进程在等文件描述符 3 上的数据;如果停在 connect(...),说明在等网络连接建立;如果停在 futex(...),说明在等多线程之间的锁。
只想看个大概,不想被海量输出淹没,可以加 -c 做汇总统计:
strace -c -p <PID>
跑几十秒按 Ctrl+C 退出,它会列出每个系统调用花了多少时间。哪个调用占了 90% 以上的时间,问题基本就在哪里。这个方法对“程序没报错、就是慢”的情况特别管用,因为日志里往往什么都没有,但系统调用不会说谎。
注意,strace 附加到线上进程会让它稍微变慢一点,生产环境用的时候尽量只跟几十秒,拿到结论就停。
第三步:按顺序发信号,别一上来就 kill -9
确认进程确实卡死、也知道卡在哪里之后,再动手。信号要按“温和到强硬”的顺序来:
kill <PID>(默认 SIGTERM):请进程自己退出。写得规范的程序收到 SIGTERM 会先保存状态、释放资源再退出。- 等几秒没反应,再试
kill -INT <PID>(SIGINT),相当于按了一次 Ctrl+C。 - 还不行,最后才用
kill -9 <PID>(SIGKILL):内核直接把进程干掉,进程连清理现场的机会都没有,打开的文件、没写完的数据都可能留在半截状态。
很多“重启后数据丢了”的事故,根源就是跳过了前两步。kill -9 是最后手段,不是第一选择。
僵尸进程不用慌
ps 里看到 Z 状态的进程,说明它已经退出了,只是父进程还没来“收尸”。僵尸进程本身不占内存、不耗 CPU,只占一个进程表项。杀僵尸进程没用,因为它已经死了;正确做法是找到它的父进程(ps -o ppid= -p <僵尸PID>),让父进程回收它,或者重启父进程。如果父进程是 init 或 systemd,一般不用管,系统会定期回收。
小结
进程卡住时,先 ps 看状态,再 cat /proc/<PID>/status 看细节,接着 strace -p 看它到底在等什么,最后按 SIGTERM → SIGINT → SIGKILL 的顺序发信号。把“卡住”讲清楚,比直接杀掉重开更省时间——同样的卡法,下次你一眼就能认出来。
- 点赞
- 收藏
- 关注作者
评论(0)