如何避免僵尸进程

一、什么是僵尸进程?

在 Linux 中,子进程调用 exit() 后,内核会保留其 PID、退出码等信息,直到父进程通过 wait()waitpid() 取走。

如果父进程迟迟不取,子进程就处于 Z (zombie/defunct) 状态,俗称“僵尸进程”。

僵尸不耗内存,但占用进程表项;大量堆积会导致 fork: Resource temporarily unavailable

二、僵尸进程的产生链路

  1. 父进程 fork()/multiprocessing.Process.start()/subprocess.Popen() 创建子进程。
  2. 子进程 正常结束被 OOM-killer 杀死(SIGKILL 9)。
  3. 父进程 未调用 wait/waitpid未注册 SIGCHLD 处理器。
  4. 子进程变成僵尸,PID 仍挂在进程表。

三、Python 中 3 种“零僵尸”写法

方案代码示例适用场景
1. 手动 joinp = Process(...); p.start(); p.join()少量子进程
2. with ProcessPoolExecutorwith ProcessPoolExecutor() as pool: pool.map(...)并行任务
3. SIG_IGNsignal.signal(signal.SIGCHLD, signal.SIG_IGN)Unix 常驻服务

四、实战:OOM 场景对比

4.1 不收割 → 僵尸堆积
# oom_zombie.py
import multiprocessing as mp

def leak():
    _ = [bytearray(1024*1024) for _ in range(10000)]

if __name__ == "__main__":
    mp.Process(target=leak).start()  # 不 join
    input("press enter to quit...")

运行:

systemd-run --scope -p MemoryMax=100M python oom_zombie.py

再开终端:

watch -n1 'ps -eo pid,ppid,state,comm | grep python'

会看到 <defunct> 僵尸。

 9530    9529 Z python3 <defunct>
4.2 with 上下文 → 零僵尸
# oom_with_ctx.py
from concurrent.futures import ProcessPoolExecutor
import os

def leak():
    _ = [bytearray(1024*1024) for _ in range(10000)]

if __name__ == "__main__":
    print("Parent PID:", os.getpid())
    with ProcessPoolExecutor(max_workers=2) as pool:
        pool.submit(leak)
        pool.submit(leak)
    print("All reaped, no zombies.")

五、小结

✅ 永远用 with:ProcessPoolExecutor、Pool、Popen
✅ 或显式 join() / wait()
✅ Unix 常驻服务可兜底 SIGCHLD = SIG_IGN
✅ 监控:ps -eo pid,ppid,state,comm | grep 'Z'
✅ 减少 OOM:控制并发、量化模型、加 swap

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Michael阿明

如果可以,请点赞留言支持我哦!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值