Subprocess.Popen启动脚本随主脚本终止问题求助
这个问题我之前也碰到过!本质是子进程和父进程共享了进程组/会话,当script1退出时,终端会给整个进程组发送SIGHUP信号,导致script2也跟着终止。另外,子进程如果还连着父进程的标准输入输出,父进程关闭这些文件描述符也可能让子进程异常退出。
下面给你几个靠谱的解决方案,按推荐程度排序:
1. 改进subprocess.Popen参数(最推荐,无需修改script2)
给Popen加上start_new_session=True,让子进程创建独立的会话,脱离父进程的进程组;同时重定向标准输入输出到/dev/null,避免终端依赖。代码示例:
import subprocess import os # 启动script2,完全脱离父进程控制 subprocess.Popen( ["python", "script2.py"], start_new_session=True, # 创建新会话,子进程不再属于父进程组 stdout=open(os.devnull, 'w'), # 重定向标准输出到空设备 stderr=open(os.devnull, 'w'), # 重定向标准错误到空设备 stdin=open(os.devnull, 'r') # 重定向标准输入到空设备 )
原理:start_new_session=True会让子进程调用setsid()系统调用,成为新会话的首进程,这样父进程退出时,子进程不会收到SIGHUP信号。重定向标准流是为了切断子进程和父进程终端的关联,防止终端关闭影响子进程。
2. 使用nohup命令(适合Linux/macOS)
借助系统的nohup命令,它的作用就是让进程忽略SIGHUP信号,即使父进程退出也能继续运行。代码示例:
import subprocess subprocess.Popen( ["nohup", "python", "script2.py"], shell=False, # 这里可以不用shell=True,更安全 stdout=open('script2.log', 'a'), # 把输出写到日志文件 stderr=subprocess.STDOUT # 把错误输出合并到标准输出 )
如果习惯用shell的&后台运行,也可以这么写(注意shell=True的安全风险,参数是固定值的话没问题):
subprocess.Popen("nohup python script2.py &", shell=True)
nohup默认会把输出写到当前目录的nohup.out,你可以自己指定日志文件,方便后续排查问题。
3. 把script2改成守护进程(需要修改script2)
如果允许修改script2的代码,可以用python-daemon库把它变成守护进程,启动后自动脱离终端和父进程。
首先安装库:
pip install python-daemon
然后修改script2的代码:
import daemon import time def update_mysql(): # 你的无限循环逻辑,比如更新数据库 while True: print("Updating MySQL...") # 这里写你的数据库更新代码 time.sleep(60) # 模拟间隔 if __name__ == "__main__": with daemon.DaemonContext(): update_mysql()
这样script1用普通的subprocess.Popen启动script2后,script2会自动变成守护进程,script1退出后它会继续在后台运行。
针对Windows系统的特殊处理
如果是Windows环境,start_new_session=True的效果有限,可以用CREATE_NEW_PROCESS_GROUP参数,或者借助start命令:
import subprocess subprocess.Popen( ["python", "script2.py"], creationflags=subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS )
或者用start命令:
subprocess.Popen("start /B python script2.py", shell=True)
/B表示不打开新窗口,后台运行。
内容的提问来源于stack exchange,提问作者strangethingspy

