在 Linux 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程。当用ps命令观察进程的执行状态时,看到这些进程的状态栏为defunct。僵尸进程是一个早已死亡的进程,但在进程表(processs table)中仍占了一个位置(slot)。
如果父进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统的进程表容量是有限的,所能使用的进程号也是有限的,如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。
所以,defunct进程不仅占用系统的内存资源,影响系统的性能,而且如果其数目太多,还会导致系统瘫痪。而且由于调度程序无法选中Defunct 进程,所以不能用kill命令删除Defunct 进程。下面我们来看看如何查询僵尸进程和相关的处理手段。
1.查找僵尸进程的方法
可以使用top命令进行查看,右上角的zombie就是僵尸进程数,如果看到数值不为0,可以通过下面命令查看具体的僵尸进程情况
ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'defunct僵尸进程可以用过下面命令进行检查
ps -A|grep defunct2.杀掉父进程来回收僵尸进程
可以通过以下方法进行僵尸进程的清除,一般僵尸进程不能杀掉,我们需要kill掉它的父进程(ppid)
ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]' | awk '{print $2}' | xargs kill -93.恢复挂起的父进程来回收僵尸进程
我们可以使用SIGCONT信号来恢复挂起的父进程,然后杀掉父进程的方式来回收僵尸进程
给父进程发送 SIGCONT (kill -18) 可以将父进程从暂停中恢复,然后继续 wait() 子进程正常结束。
可以使用如下进程查询父进程是否挂起
ps -p <ppid> -o stat如果状态是 T,表示进程被暂停,可以使用 SIGCONT 恢复
kill -18 <ppid>父进程恢复以后然后父进程可以继续wait() 子进程结束,但是这种方式并不是所有场景有效,可能由于程序本身原因父进程再次挂起。
4.卡在存储上的僵尸进程回收
cat /proc/<ppid>/status如果看到D状态,这种情况下可以df或者ls挂载到存储的目录是否卡死,如果是的可以清理挂载点,如果不行先强行取消挂载,可以使用umount和fuser命令等。
5.卡在设备锁上的僵尸进程回收
我们常见的是gpu死锁
cat /proc/<ppid>/status查看堆栈
cat /proc/<ppid>/stack查看子进程的堆栈
ps -ef --forest | grep <ppid>可以使用fuser查看是哪些进程使用gpu
fuser -v /dev/nvidia*然后可以尝试杀掉这些进程或者直接重启gpu
6.容器中的僵尸进程回收
cat /proc/<ppid>/cgroup可以根据我此前的笔记查找对应的pod,https://sulao.cn/post/985,然后可以杀掉这个重启
nerdctl -n k8s.io rm -f <pod_id>可以尝试下杀掉所有异常容器
nerdctl -n k8s.io ps -a | grep -E 'Exited|Created|Dead|Paused' | awk '{print $1}' | xargs -r nerdctl -n k8s.io rm -f
7.重启操作系统回收僵尸进程
上述所有操作都没有效果,最终也只能重启操作系统来回收僵尸进程。
内容版权声明:除非注明,否则皆为本站原创文章。
评论列表