有没有办法让os.killpg不杀掉调用它的脚本?

6 投票
4 回答
6753 浏览
提问于 2025-04-18 07:19

我有一个子进程,它会打开其他进程。

我用 os.killpg(os.getpgid(subOut.pid), signal.SIGTERM) 来结束整个进程组,但这样会把我的Python脚本也杀掉。即使我从第二个Python脚本中调用 os.killpg,也会把第二个脚本一起杀掉。有没有办法让 os.killpg 不影响到这个脚本呢?

另一种解决办法是逐个结束每个子进程。不过,即使使用

p = psutil.Process(subOut.pid)
child_pid = p.children(recursive=True)
for pid in child_pid:
    os.kill(pid.pid, signal.SIGTERM)

也无法正确获取所有子进程的ID。

你知道他们怎么说的……不要杀掉调用你的脚本……

4 个回答

0

简单的方法是在发送信号之前,让父进程忽略这个信号。

# Tell this (parent) process to ignore the signal
old_handler = signal.signal(sig, signal.SIG_IGN)

# Send the signal to our process group and
# wait for them all to exit.
os.killpg(os.getpgid(0), sig)
while os.wait() != -1:
   pass

# Restore the handler
signal.signal(sig, old_handler)
0

atleta上面的回答对我有用,但在调用Popen时,preexec_fn参数应该设置为setpgrp,而不是setgrp。

subOut = subprocess.Popen(['your', 'subprocess', ...], preexec_fn=os.setpgrp)

我把这个作为回答发出来,而不是在atleta的回答下评论,因为我还没有评论的权限。

0

创建一个进程组,把调用这个进程的所有直接子进程都放进去,方法如下:

p1 = subprocess.Popen(cmd1)
os.setpgrp(p1.pid, 0) #It will create process group with id same as p1.pid
p2 = subprocess.Popen(cmd2)
os.setpgrp(p2.pid, os.getpgid(p1.pid))

pn = subprocess.Popen(cmdn)
os.setpgrp(pn.pid, os.getpgid(p1.pid))

#Kill all the children and their process tree using following command
os.killpg(os.getpgid(p1.pid), signal.SIGKILL)

这样做会结束整个进程树,除了它自己以外的所有进程都会被杀掉。

9

虽然我来得有点晚,但因为我在找相关问题时谷歌把我带到了这里,所以我想分享一下。你的脚本被杀掉的原因是,它的子进程默认会继承它的组 ID。不过,你可以告诉 subprocess.Popen 创建一个新的进程组给你的子进程。这个过程有点复杂:你需要把 os.setpgrp 作为 preexec_fn 参数传进去。这会在新创建的(分叉的)进程中调用 setpgrp(不带任何参数),这样就会把新进程的组 ID 设置为新进程的进程 ID(这样就创建了一个新组)。文档提到在多线程代码中可能会出现死锁。作为替代方案,你可以使用 start_new_session=True,但这不仅会创建一个新的进程组,还会创建一个新的会话。(这意味着如果你在脚本运行时关闭终端会话,子进程不会被终止。这可能会是个问题,也可能不是。)

另外,如果你在 Windows 系统上,你可以简单地在 creationflag 参数中传入 subprocess.CREATE_NEW_PROCESS_GROUP

下面是详细的代码示例:

subOut = subprocess.Popen(['your', 'subprocess', ...], preexec_fn=os.setpgrp)

# when it's time to kill
os.killpg(os.getpgid(subOut.pid), signal.SIGTERM)

撰写回答