簡體   English   中英

當父進程死亡時,如何殺死用 subprocess.check_output() 創建的 python 子進程?

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

我在 linux 機器上運行一個 python 腳本,它使用 subprocess.check_output() 創建一個子進程,如下所示:

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

問題是即使父進程死了,子進程仍在運行。 當父母去世時,有什么辦法可以殺死子進程嗎?

是的,您可以通過兩種方法實現這一點。 他們都要求你使用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()

這將捕獲 sigint(鍵盤中斷)和 sigterm,但不會捕獲 sigkill(如果您使用 -9 終止腳本)。

另一種方法稍微復雜一些,它使用 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))

您的問題在於使用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

為了確保你在退出時殺死孩子:

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)

不知道具體細節,但最好的方法仍然是用信號捕獲錯誤(甚至可能是所有錯誤)並終止那里的任何剩余進程。

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()

這只是一個模型,您需要捕獲的不僅僅是 SIGINT,但這個想法可能會讓您開始,並且您仍然需要以某種方式檢查生成的進程。

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

我建議重寫一個個性化版本的check_output原因,因為我剛剛意識到 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

並用它做任何你想做的事情,也許將活動執行存儲在一個臨時變量中,並在退出時用信號或其他方式終止它們,以檢測主循環的錯誤/關閉。

最后,您仍然需要在主應用程序中捕獲終止以安全地殺死任何孩子,解決此問題的最佳方法是使用try & exceptsignal

從 Python 3.2 開始,有一種非常簡單的方法可以做到這一點:

from subprocess import Popen

with Popen(["sleep", "60"]) as process:
    print(f"Just launched server with PID {process.pid}")

我認為這對於大多數用例來說是最好的,因為它簡單且可移植,並且避免了對全局狀態的任何依賴。

如果這個解決方案不夠強大,那么我建議查看關於這個問題或Python的其他答案和討論:如何在父進程死亡時殺死子進程? ,因為有很多巧妙的方法來解決這個問題,這些方法在可移植性、彈性和簡單性方面提供了不同的權衡。 😊

您可以手動執行以下操作:

ps aux | grep <process name>

獲取PID(第二列)和

kill -9 <PID> -9 是強制殺掉

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM