簡體   English   中英

當父離開僵屍時,Python subprocess.communicate會掛起

[英]Python subprocess.communicate hangs when parent leaves zombies

我正在嘗試使用Popen創建一個子進程A以及一個使用Popen.communicate通信的線程。 主進程將使用具有指定超時的Thread.join在線程上等待,並在超時到期后終止A ,這應該導致線程也死掉。

但是,當A本身產生的子進程BCD不同於拒絕死亡的A不同進程組時,這似乎不起作用。 即使在A已經死亡並標記為已失效之后,即使在主進程使用os.waitpid()獲得A以使其不再存在之后,該線程也拒絕與主線程連接。

只有在所有的孩子, BCD被殺之后, Popen.communicate終於回來了。

這個行為實際上是從模塊中預期的嗎? 在某些情況下,遞歸等待可能很有用,但它肯定不適合作為Popen.communicate的默認行為。 如果這是預期的行為,有沒有辦法覆蓋它?

這是一個非常簡單的例子:

from subprocess import PIPE, Popen
from threading import Thread
import os
import time
import signal

DEVNULL = open(os.devnull, 'w')

proc = Popen(["/bin/bash"], stdin=PIPE, stdout=PIPE,
             stderr=DEVNULL, start_new_session=True)


def thread_function():
    print("Entering thread")
    return proc.communicate(input=b"nohup sleep 100 &\nexit\n")


thread = Thread(target=thread_function)
thread.start()
time.sleep(1)
proc.kill()
while True:
    thread.join(timeout=5)
    if not thread.is_alive():
        break
    print("Thread still alive")

這是在Linux上。

我認為這來自於在Linux中編寫popen.communicate方法的一種相當自然的方式。 Proc.communicate()似乎讀取stdin文件描述符,當進程終止時它將返回EOF。 然后它等待獲取進程的退出代碼。

在您的示例中,sleep進程從bash進程繼承stdin文件描述符。 因此,當bash進程終止時,popen.communicate沒有在stdin管道上獲得EOF,因為睡眠仍然打開它。 解決此問題的最簡單方法是將通信線路更改為:

return proc.communicate(input=b"nohup sleep 100 >/dev/null&\nexit\n")

這會導致你的線程在bash死亡后立即結束...由於退出,而不是你的proc.kill,在這種情況下。 但是,如果使用exit語句或proc.kill調用,則在bash死后,sleep仍在運行。 如果你想殺死睡眠,我會用

os.killpg(proc.pid,15)

而不是proc.kill()。 殺死B,C和D的更一般的問題是,如果他們改變組是一個更復雜的問題。

附加數據:我找不到這種proc.communicate方法的官方文檔,但我忘記了最明顯的地方:-)我在這個答案的幫助下找到了它。 溝通文件說:

與流程交互:將數據發送到stdin。 從stdout和stderr讀取數據,直到達到文件結尾。 等待進程終止。

您在步驟2中遇到困難:讀取直到文件結束,因為睡眠會使管道保持打開狀態。

暫無
暫無

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

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