Linux下如何监控Python Popen子进程并在任一进程结束后终止其余进程
问题描述
我编写了一段Python代码,可在不同Shell窗口中同时运行带有不同参数的同一Python脚本,代码如下:
import subprocess import shlex if __name__ == '__main__': arg_list = ["gnome-terminal -- python3 teste.py ./get_data/datasets/training_out.xlsx 140.10.61.1 0 2 102 4 OUTPUT 0 632 MEMORY 0 3885 1 0", "gnome-terminal -- python3 teste.py ./get_data/datasets/training_out2.xlsx 140.10.61.1 0 2 102 1 OUTPUT 0 272 MEMORY 0 3885 1 1", "gnome-terminal -- python3 teste.py ./get_data/datasets/training_in.xlsx 140.10.61.1 0 2 102 2 INPUT 0 632 MEMORY 0 3885 1 2", "gnome-terminal -- python3 teste.py ./get_data/datasets/training_in2.xlsx 140.10.61.1 0 2 102 2 INPUT 0 272 MEMORY 0 3885 1 3", "gnome-terminal -- python3 teste.py ./get_data/datasets/training_in3.xlsx 140.10.61.1 0 2 102 1 INPUT 0 274 MEMORY 0 3885 1 4" ] p1 = subprocess.Popen(shlex.split(arg_list[0])) p2 = subprocess.Popen(shlex.split(arg_list[1])) p3 = subprocess.Popen(shlex.split(arg_list[2])) p4 = subprocess.Popen(shlex.split(arg_list[3])) p5 = subprocess.Popen(shlex.split(arg_list[4]))
但我需要监控这些子进程,当任一子进程执行完成后,终止其余所有运行中的子进程。我尝试使用.wait()和.poll()方法,但它们仅在gnome terminal启动后就改变状态,而非在终端内的脚本执行结束时变化,请问在Linux下该如何实现这种监控?
解决方案
方法一:利用gnome-terminal的--wait参数
gnome-terminal提供了--wait参数,该参数会让终端窗口保持打开状态,直到内部执行的命令完全结束。此时subprocess启动的gnome-terminal进程的结束状态会与内部teste.py脚本的结束状态同步,这样就能通过.wait()或.poll()正确监控脚本的执行完成事件。
修改后的代码示例:
import subprocess import shlex import signal if __name__ == '__main__': # 为每个gnome-terminal命令添加--wait参数 arg_list = [ "gnome-terminal --wait -- python3 teste.py ./get_data/datasets/training_out.xlsx 140.10.61.1 0 2 102 4 OUTPUT 0 632 MEMORY 0 3885 1 0", "gnome-terminal --wait -- python3 teste.py ./get_data/datasets/training_out2.xlsx 140.10.61.1 0 2 102 1 OUTPUT 0 272 MEMORY 0 3885 1 1", "gnome-terminal --wait -- python3 teste.py ./get_data/datasets/training_in.xlsx 140.10.61.1 0 2 102 2 INPUT 0 632 MEMORY 0 3885 1 2", "gnome-terminal --wait -- python3 teste.py ./get_data/datasets/training_in2.xlsx 140.10.61.1 0 2 102 2 INPUT 0 272 MEMORY 0 3885 1 3", "gnome-terminal --wait -- python3 teste.py ./get_data/datasets/training_in3.xlsx 140.10.61.1 0 2 102 1 INPUT 0 274 MEMORY 0 3885 1 4" ] # 启动所有进程并保存进程对象 processes = [] for cmd in arg_list: proc = subprocess.Popen(shlex.split(cmd)) processes.append(proc) # 监控进程状态,任一进程结束则终止其余进程 try: while processes: for idx, proc in enumerate(processes): # 检查进程是否已结束 if proc.poll() is not None: print(f"进程 {idx} 执行完成,正在终止其余进程...") # 终止所有未结束的进程 for p in processes: if p.poll() is None: p.send_signal(signal.SIGTERM) processes = [] break except KeyboardInterrupt: # 处理手动中断,清理所有进程 for p in processes: if p.poll() is None: p.send_signal(signal.SIGTERM)
方法二:直接监控teste.py进程
如果不想依赖gnome-terminal的参数,可以直接追踪实际运行的teste.py进程。通过psutil库可以方便地查找和监控进程状态,当任一teste.py进程结束时,终止剩余的teste.py进程及对应的终端窗口。
步骤说明
- 先安装psutil库:
pip install psutil - 启动终端进程后,获取所有
teste.py的PID - 循环监控这些PID的运行状态,一旦有进程结束,立即终止剩余进程
示例代码:
import subprocess import shlex import psutil import os import signal def get_teste_pids(): """获取当前运行的所有teste.py进程PID""" pids = [] for proc in psutil.process_iter(['pid', 'cmdline']): try: cmdline = proc.info['cmdline'] # 判断是否是目标teste.py进程 if cmdline and 'python3' in cmdline[0] and 'teste.py' in cmdline[1]: pids.append(proc.info['pid']) except (psutil.NoSuchProcess, psutil.AccessDenied): continue return pids if __name__ == '__main__': arg_list = [ "gnome-terminal -- python3 teste.py ./get_data/datasets/training_out.xlsx 140.10.61.1 0 2 102 4 OUTPUT 0 632 MEMORY 0 3885 1 0", "gnome-terminal -- python3 teste.py ./get_data/datasets/training_out2.xlsx 140.10.61.1 0 2 102 1 OUTPUT 0 272 MEMORY 0 3885 1 1", "gnome-terminal -- python3 teste.py ./get_data/datasets/training_in.xlsx 140.10.61.1 0 2 102 2 INPUT 0 632 MEMORY 0 3885 1 2", "gnome-terminal -- python3 teste.py ./get_data/datasets/training_in2.xlsx 140.10.61.1 0 2 102 2 INPUT 0 272 MEMORY 0 3885 1 3", "gnome-terminal -- python3 teste.py ./get_data/datasets/training_in3.xlsx 140.10.61.1 0 2 102 1 INPUT 0 274 MEMORY 0 3885 1 4" ] # 启动所有终端进程 term_processes = [] for cmd in arg_list: proc = subprocess.Popen(shlex.split(cmd)) term_processes.append(proc) # 获取初始的teste.py进程PID列表 initial_teste_pids = get_teste_pids() print(f"正在监控的teste.py进程PID: {initial_teste_pids}") try: while initial_teste_pids: # 遍历检查每个PID的状态 for pid in initial_teste_pids[:]: if not psutil.pid_exists(pid): print(f"teste.py进程 {pid} 执行完成,正在终止其余进程...") # 终止剩余的teste.py进程 for p in initial_teste_pids: if psutil.pid_exists(p): os.kill(p, signal.SIGTERM) # 终止所有终端进程 for term_proc in term_processes: if term_proc.poll() is None: term_proc.send_signal(signal.SIGTERM) initial_teste_pids = [] break except KeyboardInterrupt: # 手动中断时清理所有进程 for pid in initial_teste_pids: if psutil.pid_exists(pid): os.kill(pid, signal.SIGTERM) for term_proc in term_processes: if term_proc.poll() is None: term_proc.send_signal(signal.SIGTERM)
内容的提问来源于stack exchange,提问作者guilhermeguim
相关产品推荐
相关产品推荐

