简介

僵尸进程(zombie process)是指一个已经终止但仍然在进程表中保留条目的进程。正常情况下,当一个进程完成执行并退出时,操作系统会通过父进程调用的wait()或waitpid()系统调用来收集该子进程的退出状态。如果父进程未及时调用这些函数,子进程的状态信息就无法从内核中移除,导致进程在内核中以“僵尸”状态存在。此状态下的子进程,使用kill -9 就是“鞭尸”,是无法移除僵尸进程的

僵尸进程的主要特点是它们不再消耗CPU或内存资源,因为其已终止,只保留一个进程号(PID)和少量的状态信息。然而,过多的僵尸进程会使系统的进程号资源耗尽,阻止新进程的创建,影响系统稳定性。

产生僵尸进程的常见原因包括:父进程未能及时俘获到子进程得退出信息,并对其进程信息进行处理,导致子进程进入、“僵尸状态”。本文以postgres 进行举例

“t” 状态解决办法

使用gdb挂住postgres(主进程),此时数据库得主进程处于t状态。将子进程kill 掉此时,子进程就会变成僵尸状态。

postgresql僵尸进程的处理思路_postgresql


此状态下现存的客户端进程会一直保持写入状态。Wal文件持续增长中。此时主进程状态处于t状态。

ps -o pid,ppid,cmd,state,etime -p 1797

t状态表示进程被跟踪并停止(Traced and stopped)

使用ps -ef |grep 1797查看调用postgres(主进程的程序)的进程 ,将其kill 掉,postgresql运行会恢复正常。子进程会被重新拉起。此时主进程状态会恢复到S状态。

postgresql僵尸进程的处理思路_postgresql_02

“T” 状态解决办法

“T”状态 表示进程被停止。

使用kill -19 1797 stop掉主进程,然后kill -9杀掉所有的子进程。模拟僵尸僵尸状态

postgresql僵尸进程的处理思路_postgresql_03


此时父进程状态为T 状态,kill -19 之后,父进程将无法再处理子进程的退出消息。此时对父进程发送继续进程的指令kill -18 ,此时父进程会重新运行,并进行处理消息列表的信号。

postgresql僵尸进程的处理思路_postgresql_04

“Z” 僵尸状态

进程已经终止,但父进程尚未回收它的退出状态,导致该进程变成僵尸进程。

postgresql僵尸进程的处理思路_postgresql_05

,当然在以上的两个状态中,子进程也是处于“Z”状态,但是我们可以通过父进程进行判断僵尸进程的原因,使用危害较小的方式进行处理,如果此时的父进程state处于正常状态,但是子进程处于“Z”状态,此时引发原因就变得难以追踪。

模拟父进程没通过wait(),waipid()进行处理子进程退出信息,引发僵尸进程

编辑脚本zombie_process.py

使用一下指令进行调用

python3 zombie_process.py

脚本内容如下:

import os
import time

def create_zombie():
    pid = os.fork()  # 创建子进程
    if pid > 0:
        # 父进程:故意不处理子进程的退出状态
        print(f"父进程 PID: {os.getpid()}")
        print(f"子进程 PID: {pid}")
        print("子进程变为僵尸进程(父进程不回收子进程)。")
        time.sleep(60)  # 父进程休眠,保持子进程为僵尸进程
    elif pid == 0:
        # 子进程
        print(f"子进程 PID: {os.getpid()} 终止")
        os._exit(0)  # 子进程结束

if __name__ == "__main__":
    create_zombie()

postgresql僵尸进程的处理思路_postgresql_06


此时子进程处于“Z”状态,但是父进程确实正常state 状态,此时需要。kill父进程,让其由systemd一号进程进行接管,编处理僵尸进程的退出状况。