关闭Python进程后保持子进程持续运行的实现问题
解决主进程终止后子进程继续运行的问题
这个问题本质是进程组与信号传递导致的:当你用shell=True时,实际启动的是一个shell进程,它才是yes命令的父进程;而主进程终止时,shell进程会收到SIGHUP信号(终端挂起信号),进而把这个信号转发给它的子进程yes,导致yes跟着退出。哪怕不用shell=True,子进程和主进程默认在同一个进程组,主进程终止(比如关闭终端)时,整个进程组都会收到SIGHUP,同样会终止子进程。
下面给你几个可靠的解决办法:
方法1:脱离进程组(推荐,无需shell)
直接给Popen加上start_new_session=True参数,让子进程成为新会话的领头进程,脱离主进程的进程组,这样主进程终止时,子进程不会收到SIGHUP信号。同时尽量避免用shell=True,减少不必要的中间进程:
from subprocess import Popen args = ['yes'] # 启动子进程并创建新会话 Popen(args, stdout=None, stdin=None, stderr=None, start_new_session=True)
方法2:结合nohup使用(适合必须用shell的场景)
如果因为某些原因必须用shell=True,可以用nohup命令让yes忽略SIGHUP信号,同时重定向输出避免生成nohup.out文件:
from subprocess import Popen # nohup让进程忽略SIGHUP,&让shell在后台启动进程,重定向输出到/dev/null Popen('nohup yes > /dev/null 2>&1 &', shell=True)
方法3:创建孤儿进程(底层实现方式)
通过两次fork创建孤儿进程,让最终的子进程被init或systemd接管,彻底脱离主进程的控制:
import os import subprocess if os.fork() == 0: # 第一次fork的子进程,创建新会话 os.setsid() if os.fork() == 0: # 第二次fork的孙子进程,此时父进程会变成init/systemd,成为孤儿进程 subprocess.run(['yes'], stdout=None, stdin=None, stderr=None) # 第一个子进程退出,避免成为僵尸进程 os._exit(0) # 主进程等待第一个子进程退出 os.wait()
简单总结:优先用方法1,简洁且避免shell带来的额外问题;如果必须用shell,选方法2;方法3适合需要更底层控制的场景。
内容的提问来源于stack exchange,提问作者Chaos
相关产品推荐
相关产品推荐

