1. 什么是僵尸进程
进程的创建过程:
- 父进程调用
fork()
创建子进程 - 子进程执行
exec()
加载新程序 - 子进程结束执行,调用
exit()
或返回 - 父进程调用
wait()
或 waitpid()
如果父进程没有调用 wait()
或 waitpid()
,子进程结束后仍然保留在系统中,成为僵尸进程。
2. 怎么查看僵尸进程
可以使用 ps
命令查看僵尸进程:
1
| ps -A -ostat,ppid,pid,cmd | grep -e'^[Zz]'
|
1
2
3
4
| Zl 2062969 2068159 [python] <defunct>
Zl 2062969 2074157 [python] <defunct>
ZNl 2062969 2081477 [ray::WorkerDict] <defunct>
ZNl 2062969 2081478 [ray::WorkerDict] <defunct>
|
第一列是进程的状态,第二列是父进程 ID (PPID),第三列是僵尸进程的 ID (PID),第四列是命令。
3. 怎么清理僵尸进程
3.1 直接杀死父进程
僵尸进程本身无法被清理,只能清理其父进程来清理。
简单点可以直接 kill 父进程:
也可以批量清理所有僵尸进程的父进程:
1
| ps -A -ostat,ppid,pid,cmd | grep -e'^[Zz]' |awk '{print $2}' | xargs kill -9
|
3.2 恢复父进程回收僵尸进程
给父进程发送 SIGCONT (kill -18) 可以将父进程从暂停中恢复,然后继续 wait()
子进程正常结束。
1
| ps -p <parent_pid> -o stat
|
如果状态是 T
,表示进程被暂停,可以使用 SIGCONT 恢复。
3.3 回收进程所在容器
1
| export parent_pid=2062969
|
1
| cat /proc/${parent_pid}/cgroup
|
1
| 0::/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod28df2390_283c_4962_b6f6_4efaafe529ec.slice/cri-containerd-c94382533ede3ae949b06562b8f347a0656a7446403722d4b026256c19e6aea1.scope
|
截取 containerd 后面的部分字符串用于查找容器。
1
| nerdctl -n k8s.io ps -a | grep c94382533
|
1
| c94382533ede docker.io/3257a037b275eb6ca6f75d86f2bc63b9:v1.0 "/bin/bash -c bash r…" 14 hours ago Created k8s://default/elastic-job-261548-edljob-worker-0/main
|
1
| nerdctl -n k8s.io rm -f c94382533ede
|
1
| nerdctl -n k8s.io ps -a | grep -E 'Exited|Created|Dead|Paused' | awk '{print $1}' | xargs -r nerdctl -n k8s.io rm -f
|
3.4 进程卡存储存储上
1
| export parent_pid=1203391
|
1
| cat /proc/${parent_pid}/status
|
1
2
3
4
5
6
| Name: python3.10
State: D (disk sleep)
Tgid: 1203391
Ngid: 1203391
Pid: 1203391
PPid: 1197458
|
这种情况下,可以尝试查看一下 df -h
是不是卡死。如果是,那么清理挂载点即可。
1
| opscli task -f ~/.ops/tasks/clear-mount.yaml
|
3.5 进程卡在设备锁上
1
| export parent_pid=2062969
|
1
| cat /proc/${parent_pid}/status
|
1
2
3
4
5
6
| Name: bash
State: S (sleeping)
Tgid: 2062969
Ngid: 0
Pid: 2062969
PPid: 2062945
|
1
| cat /proc/${parent_pid}/stack
|
1
2
3
4
5
6
7
8
| [<0>] do_wait+0x1c7/0x230
[<0>] kernel_wait4+0xaf/0x150
[<0>] zap_pid_ns_processes+0x111/0x1b0
[<0>] do_exit+0xa7c/0xac0
[<0>] do_group_exit+0x47/0xb0
[<0>] __x64_sys_exit_group+0x18/0x20
[<0>] do_syscall_64+0x57/0x190
[<0>] entry_SYSCALL_64_after_hwframe+0x44/0xa9
|
1
| ps -ef --forest | grep ${parent_pid}
|
1
2
3
4
5
| root 2062969 1 0 02:02 ? 00:00:01 [bash]
root 2068159 2062969 12 02:03 ? 01:43:37 \_ [python] <defunct>
root 2074157 2062969 0 02:03 ? 00:00:29 \_ [python] <defunct>
root 2081477 2062969 39 02:04 ? 05:32:22 \_ [ray::WorkerDict] <defunct>
root 2081478 2062969 36 02:04 ? 05:11:06 \_ [ray::WorkerDict] <defunct>
|
1
| export child_pid=2068159
|
1
| cat /proc/${child_pid}/stack
|
1
2
3
| [<0>] rwsem_down_write_slowpath+0x244/0x4d0
[<0>] os_acquire_rwlock_write+0x35/0x40 [nvidia]
[<0>] _nv042313rm+0x10/0x40 [nvidia]
|
进程正在尝试获取 nvidia 提供的一个写锁,但是阻塞了。
1
| fuser -v /dev/nvidia* |awk '{for(i=1;i<=NF;i++)print "kill -9 " $i;}' | sh
|
1
2
3
| USER PID ACCESS COMMAND
/dev/nvidiactl: root F.... nvidia-smi
root F.... nvidia-smi
|
D 状态(Uninterruptible sleep) 意味着进程处于内核阻塞状态,在等待某些资源不能被中断,不能 kill ,可以升级一下驱动版本之后,进行重启。