You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask启动的Linux进程执行后变为defunct的解决方法求助

Flask子进程变为僵尸进程的解决方法

问题描述

我需要用Flask应用通过特定端点管理命令执行,同时设置第二个端点监控Flask触发的进程。目前整体思路已经明确,但遇到一个问题:进程执行完毕后,启动的进程会变成defunct(僵尸进程)。我知道这是因为创建进程的父进程(我的Flask应用)仍在运行。我尝试过不同方式启动进程,也试过强制终止,但都没用。相关代码如下:

try:
    res = Popen("my_script.sh",
        universal_newlines=True,
        start_new_session=True,
        stdout=PIPE,
        stderr=STDOUT,
        cwd="/",
        shell=True,
        env=environ.copy(),
        close_fds=True
    )
    # 该函数尝试处理进程终止逻辑
    # 必须用独立进程,这样应用才能在后台进程运行时返回响应
    multiprocessing.Process(target=_ManagerExecutor.__handle_process, args=(res,)).start()
    return result
except Exception as e:
    ...          
@classmethod
def __handle_process(cls, process: Popen):
    while True:
        output = process.stdout.readline()
        if not output:
            break
        #... 其他业务逻辑
    process.poll()
    result: bool = process.returncode != 0
    try:
        # 尝试强制杀死进程组,但无效
        killpg(getpgid(process.pid), SIGTERM)
    except Exception:
        pass

核心原因

僵尸进程产生的本质是子进程结束后,父进程未调用wait()/waitpid()回收其退出状态。当前代码中,process.poll()仅用于查询进程状态,不会释放僵尸进程的资源;而进程结束后调用killpg属于无效操作,完全无法解决问题。另外,负责处理Popen进程的multiprocessing.Process作为父进程,必须承担回收子进程资源的责任。

修复方案

方案1:使用communicate()自动回收资源

communicate()会自动读取子进程的所有输出,阻塞到进程结束后调用wait()回收资源,彻底避免僵尸进程:

修改后的__handle_process方法:

@classmethod
def __handle_process(cls, process: Popen):
    try:
        # 读取所有输出并等待进程结束,自动回收资源
        output, _ = process.communicate()
        # 逐行处理输出(按需调整)
        for line in output.splitlines():
            # ... 你的业务逻辑代码
            pass
        # 获取进程退出状态
        result: bool = process.returncode != 0
    except Exception as e:
        # 异常处理逻辑
        pass

方案2:保留逐行读取,显式调用wait()

如果需要实时处理进程输出,不能用communicate(),则在循环结束后必须显式调用process.wait()回收资源:

@classmethod
def __handle_process(cls, process: Popen):
    try:
        while True:
            output = process.stdout.readline()
            if not output:
                # 确认进程已结束
                if process.poll() is not None:
                    break
                continue
            # ... 你的业务逻辑代码
        # 显式回收子进程,消除僵尸
        process.wait()
        result: bool = process.returncode != 0
    except Exception as e:
        pass

额外优化建议

  • 移除shell=True:如果脚本路径固定,改用列表形式传参(如["/full/path/to/my_script.sh"]),减少安全风险,同时避免额外的shell子进程产生僵尸。
  • 按需使用start_new_session:如果不需要独立会话组,可去掉该参数,简化进程管理。

内容的提问来源于stack exchange,提问作者Matteo Pasini

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 00:30:18