我正在尝试使用Popen
创建子进程A
以及使用Popen.communicate
与之通信的线程。主进程将使用具有指定超时的Thread.join
在线程上等待,并在超时到期后终止A
,这将导致线程死亡。
但是,当A
本身产生的子进程B
,C
和D
与A
不同的进程组时,这似乎不起作用拒绝死即使A
已经死亡并标记为已失效,即使主进程使用A
重新使用os.waitpid()
以使其不再存在,该线程也拒绝与主线程连接。 / p>
只有在所有孩子B
,C
,D
被杀后,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上。
答案 0 :(得分:1)
我认为这来自于在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方法的官方文档,但我忘记了最明显的地方:-)我在this answer的帮助下找到了它。 docs for communicate说:
与流程交互:将数据发送到stdin。从stdout和stderr读取数据,直到达到文件结尾。等待进程终止。
你在第二步陷入困境:阅读直到文件结束,因为睡眠会使管道保持打开状态。