Linux 当父进程死亡时,如何杀死用 subprocess.check_output() 创建的 python 子进程?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19447603/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 01:07:19  来源:igfitidea点击:

How to kill a python child process created with subprocess.check_output() when the parent dies?

pythonlinuxsubprocess

提问by Clara

I am running on a linux machine a python script which creates a child process using subprocess.check_output() as it follows:

我在 linux 机器上运行一个 python 脚本,它使用 subprocess.check_output() 创建一个子进程,如下所示:

subprocess.check_output(["ls", "-l"], stderr=subprocess.STDOUT)

The problem is that even if the parent process dies, the child is still running. Is there any way I can kill the child process as well when the parent dies?

问题是即使父进程死了,子进程仍在运行。当父母去世时,有什么办法可以杀死子进程吗?

采纳答案by cdarke

Your problem is with using subprocess.check_output- you are correct, you can't get the child PID using that interface. Use Popen instead:

您的问题在于使用subprocess.check_output- 您是对的,您无法使用该接口获取子 PID。改用 Popen:

proc = subprocess.Popen(["ls", "-l"], stdout=PIPE, stderr=PIPE)

# Here you can get the PID
global child_pid
child_pid = proc.pid

# Now we can wait for the child to complete
(output, error) = proc.communicate()

if error:
    print "error:", error

print "output:", output

To make sure you kill the child on exit:

为了确保你在退出时杀死孩子:

import os
import signal
def kill_child():
    if child_pid is None:
        pass
    else:
        os.kill(child_pid, signal.SIGTERM)

import atexit
atexit.register(kill_child)

回答by Ciprian Tarta

Manually you could do this:

您可以手动执行以下操作:

ps aux | grep <process name>

ps aux | grep <process name>

get the PID(second column) and

获取PID(第二列)和

kill -9 <PID>-9 is to force killing it

kill -9 <PID>-9 是强制杀死它

回答by Torxed

Don't know the specifics, but the best way is still to catch errors (and perhaps even all errors) with signal and terminate any remaining processes there.

不知道具体细节,但最好的方法仍然是用信号捕获错误(甚至可能是所有错误)并终止那里的任何剩余进程。

import signal
import sys
import subprocess
import os

def signal_handler(signal, frame):
    sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)

a = subprocess.check_output(["ls", "-l"], stderr=subprocess.STDOUT)

while 1:
    pass # Press Ctrl-C (breaks the application and is catched by signal_handler()

This is just a mockup, you'd need to catch more than just SIGINT but the idea might get you started and you'd need to check for spawned process somehow still.

这只是一个模型,您需要捕获的不仅仅是 SIGINT,但这个想法可能会让您开始,并且您仍然需要以某种方式检查生成的进程。

http://docs.python.org/2/library/os.html#os.killhttp://docs.python.org/2/library/subprocess.html#subprocess.Popen.pidhttp://docs.python.org/2/library/subprocess.html#subprocess.Popen.kill

http://docs.python.org/2/library/os.html#os.kill http://docs.python.org/2/library/subprocess.html#subprocess.Popen.pid http://docs. python.org/2/library/subprocess.html#subprocess.Popen.kill

I'd recommend rewriting a personalized version of check_outputcause as i just realized check_output is really just for simple debugging etc since you can't interact so much with it during executing..

我建议重写一个个性化版本的check_output原因,因为我刚刚意识到 check_output 实际上只是用于简单的调试等,因为在执行过程中你不能与它进行太多交互。

Rewrite check_output:

重写 check_output:

from subprocess import Popen, PIPE, STDOUT
from time import sleep, time

def checkOutput(cmd):
    a = Popen('ls -l', shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
    print(a.pid)
    start = time()
    while a.poll() == None or time()-start <= 30: #30 sec grace period
        sleep(0.25)
    if a.poll() == None:
        print('Still running, killing')
        a.kill()
    else:
        print('exit code:',a.poll())
    output = a.stdout.read()
    a.stdout.close()
    a.stdin.close()
    return output

And do whatever you'd like with it, perhaps store the active executions in a temporary variable and kill them upon exit with signal or other means of intecepting errors/shutdowns of the main loop.

并用它做任何你想做的事情,也许将活动执行存储在一个临时变量中,并在退出时用信号或其他方式来拦截主循环的错误/关闭。

In the end, you still need to catch terminations in the main application in order to safely kill any childs, the best way to approach this is with try & exceptor signal.

最后,您仍然需要在主应用程序中捕获终止以安全地杀死任何孩子,解决此问题的最佳方法是使用try & exceptor signal

回答by micromoses

Yes, you can achieve this by two methods. Both of them require you to use Popeninstead of check_output. The first is a simpler method, using try..finally, as follows:

是的,您可以通过两种方法实现这一点。它们都要求您使用Popen而不是check_output. 第一种是更简单的方法,使用try..finally,如下:

from contextlib import contextmanager

@contextmanager
def run_and_terminate_process(*args, **kwargs):
try:
    p = subprocess.Popen(*args, **kwargs)
    yield p        
finally:
    p.terminate() # send sigterm, or ...
    p.kill()      # send sigkill

def main():
    with run_and_terminate_process(args) as running_proc:
        # Your code here, such as running_proc.stdout.readline()

This will catch sigint (keyboard interrupt) and sigterm, but not sigkill (if you kill your script with -9).

这将捕获 sigint(键盘中断)和 sigterm,但不会捕获 sigkill(如果您使用 -9 终止脚本)。

The other method is a bit more complex, and uses ctypes' prctl PR_SET_PDEATHSIG. The system will send a signal to the child once the parent exits for any reason (even sigkill).

另一种方法稍微复杂一些,它使用 ctypes 的 prctl PR_SET_PDEATHSIG。一旦父母出于任何原因(甚至是 sigkill)退出,系统就会向孩子发送信号。

import signal
import ctypes
libc = ctypes.CDLL("libc.so.6")
def set_pdeathsig(sig = signal.SIGTERM):
    def callable():
        return libc.prctl(1, sig)
    return callable
p = subprocess.Popen(args, preexec_fn = set_pdeathsig(signal.SIGTERM))