Python subprocess 完整终止子进程及进程组的方法
本文介绍在Python中使用subprocess模块时,如何彻底终止由子进程派生的所有后代进程。通过设置preexec_fn为os.setpgrp创建独立进程组,并结合os.kill和os.waitpid处理信号与清理僵尸进程,解决了跨平台下的进程管理难题。
以下是本次技术调研的核心成果总结。
子进程终止的难点与进程组概念
在发布系统中,需要响应用户的中断请求。具体场景是在处理GET请求时,需要终止由subprocess模块派生的子进程。起初尝试直接使用os.kill,但发现无法杀死子进程所派生的孙子进程。经过查阅资料,了解到可以通过kill进程组来实现批量终止。然而,默认情况下,subprocess派生的进程与主程序(即当前的web.py进程)处于同一个进程组中,如果直接对该进程组发送终止信号,会导致主程序也被意外终止,这显然不符合预期。
继续查阅subprocess的官方文档,发现了一个关键参数:
subprocess.CREATE_NEW_PROCESS_GROUP 是 Popen 的 creationflags 参数,用于指定创建一个新的进程组。在使用 os.kill() 对子进程进行操作时,必须设置此标志。
需要注意的是,如果同时指定了 CREATE_NEW_CONSOLE,则该标志将被忽略。
Windows 与 Linux 下的进程组创建方案
起初认为找到了解决方案,但在实际测试中发现,该参数仅适用于 Windows 系统。考虑到 Windows 能实现的功能,Linux 系统理应也有对应的机制,于是将目光转向了 Linux 下的相关配置。
preexec_fn
再次进行搜索,原本以为需要传入一个对象,于是尝试使用 setpgid(0,0) 进行测试,但结果显示子进程仍然与主调进程属于同一个进程组。随后灵光一现,调整了参数用法。
preexec_fn = os.setpgrp
通过这种方式,成功解决了新生成进程独立成组的问题。
处理僵死进程与最终测试代码
接下来需要处理的是僵死进程(Zombie Process)的问题,通过调用 os.waitpid 即可解决。
在最初使用 waitpid 时,查阅了 Linux 手册中的相关参数说明,仍感到不放心。结果发现 Python 中的 os.waitpid 接口相对简化,参数较少且没有返回值,虽然看似简陋,但恰好满足了我的需求。
以下是经过完整测试的代码示例:
[liufeng@1.2.3.4 kill-subprocess]$ cat sub-process.py
import subprocess
import os
import time
def my_func():
#派生两个子进程,子进程里又派生几个sleep的孙子进程,主要是为了测试kill进程组。
run_str2 = '/bin/sh test.sh'
run_str = '/bin/sh test_quick.sh'
cmd2 = run_str.split()
cmd = run_str.split()
#测试了一些个preexec_fn的值,最终发现能用的,对python的对象的概念还是不理解啊,新手,新手。
#p = subprocess.Popen(cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = False, creationflags = subprocess.CREATE_NEW_PROCESS_GROUP)
#p = subprocess.Popen(cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = False, creationflags = 0)
p = subprocess.Popen(cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = False, preexec_fn = os.setpgrp )
p2 = subprocess.Popen(cmd2, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = False, preexec_fn = os.setpgrp )
#@p = subprocess.Popen(cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = False, preexec_fn = os.setpgid(0, 0) )
pid = p.pid
pgid = os.getpgid(pid)
print "pid: %d\n" %pid
print "pgid: %d\n" %pgid
return pid
pid = my_func()
#p.wait()
print "now , sleep 2s ,then , os.kill gpid %d" % pid
time.sleep(20)
a = os.kill(-pid, 9)
print "kill,return:"
print a
# kill的时候,我测试了kill 没有权限的root进程,会报错:权限不允许
# 测试了kill p p2 都可以kill
#a = os.kill(2445, 9)
#print "kill root process 2445 ,return:"
#print a
#p.wait()
#os.waitpid(pgid, 0)
# 2445 is a root process
#os.waitpid(2445, 0)
#os.waitpid(p2.pid, 0)
os.waitpid(pid, 0)
print "waitpid,return:"
print a
time.sleep(22)
print "done..."
#p.terminate()
#p.kill()
#p.wait()
#
#time.sleep(40)
#os.kill(pid, 9)
以上关于 Python subprocess 彻底杀掉所有派生子进程的方法分享完毕,希望能为读者提供参考,并欢迎大家继续支持。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













