Python应用中SIGINT后执行子进程的解决方案
解决SIGINT中断后子进程无法执行的问题
这个问题我之前也碰到过,核心原因是SIGINT信号会传递给整个进程组——当你按下Ctrl+C触发KeyboardInterrupt时,当前进程组里的所有进程(包括之后启动的子进程)都会收到这个中断信号,导致save_work()里的hadoop命令刚启动就被终止。下面给你几个实用的解决办法:
方法1:让子进程脱离当前进程组
通过修改subprocess.Popen的参数,让子进程在独立的进程组/会话中运行,这样就不会继承父进程的SIGINT信号:
跨平台方案(Unix/Linux优先,Windows需调整)
使用start_new_session=True参数,让子进程创建新的会话:
cmd = r"hadoop fs -put '{}' '{}'".format(src, dest) process = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, start_new_session=True # 关键:子进程将在新会话中运行,不受父进程组信号影响 ) # 等待子进程完成并获取输出 stdout, _ = process.communicate()
Unix/Linux专属方案
使用preexec_fn=os.setpgrp将子进程设为新进程组的组长:
import os cmd = r"hadoop fs -put '{}' '{}'".format(src, dest) process = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, preexec_fn=os.setpgrp # Unix下生效:让子进程成为新进程组的组长,脱离原进程组 ) stdout, _ = process.communicate()
方法2:临时忽略SIGINT信号再恢复
在启动子进程前,先让父进程忽略SIGINT信号,子进程启动后再恢复原来的信号处理逻辑。这样子进程启动时不会继承被忽略的信号状态,就能正常执行:
import signal class Main(object): def do_stuff(self): # 你的核心业务逻辑 ... def save_work(self): # 先保存原本的SIGINT处理函数 original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) try: # 此时启动子进程,不会收到SIGINT信号 cmd = r"hadoop fs -put '{}' '{}'".format(src, dest) process = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) stdout, _ = process.communicate() # 可以在这里处理子进程的输出 print("保存完成,输出:", stdout.decode()) finally: # 无论成功失败,都恢复原来的SIGINT处理 signal.signal(signal.SIGINT, original_sigint_handler) def __init__(self): try: self.do_stuff() except KeyboardInterrupt: print("收到中断信号,正在保存工作...") self.save_work()
方法3:借助shell命令隔离信号
如果你的场景依赖shell=True,可以在hadoop命令前加上nohup或者setsid,让子进程脱离当前进程组:
# 使用nohup让命令忽略挂起信号,同时脱离进程组 cmd = r"nohup hadoop fs -put '{}' '{}' > /dev/null 2>&1".format(src, dest) process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) process.wait()
注意:
> /dev/null 2>&1是把nohup的输出重定向到空设备,如果你需要保留输出,可以改成指定日志文件路径。
内容的提问来源于stack exchange,提问作者Stefan Papp
相关产品推荐
相关产品推荐

