子流程.Popen的标准缓冲区/标准输出永远不会冲水

2024-04-25 20:26:40 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在编写一个python守护进程(使用python3.7),它连续检查stdin上是否有可用的数据(使用select),并对其进行处理。数据可以包含非unicode字符,因此守护进程需要从sys.stdin.buffer而不是sys.stdin读取。你知道吗

这个程序实际上是有效的。但是我正在为这个程序编写一个函数测试,使用第二个python脚本,用subprocess.Popen启动守护进程,并将数据发送到它的stdin并从它的stdout读取。不知为什么,那部分不起作用。proc.readline()永远阻塞,有些proc.stdin.write永远无法到达子进程。你知道吗

简而言之,我有两个小脚本来说明这个问题。 现在,问题是第一个readline()调用测试.py正在阻塞并且永不返回,而test2.py已经向stdout写了一整行。你知道吗

# test.py
from subprocess import Popen, PIPE, TimeoutExpired
import time
import select

proc = Popen(["python", "test1.py"], stdin=PIPE, stdout=PIPE,
             bufsize=0)

print("Writing")
proc.stdin.write("blablabla\n".encode('utf-8'))
time.sleep(2)
output = proc.stdout.readline()
print("Result")
print(output)
print("Writing")
proc.stdin.write("zxczxczxczxc\n".encode('utf-8'))
time.sleep(2)
output = proc.stdout.readline()
print("Result")
print(output)
proc.terminate()
print(proc.stdout.read())
#test1.py
import select
import signal
import sys
import logging
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
log = logging.getLogger()

STOP = False


def stop(frame, signal):
    global STOP
    STOP = False


signal.signal(signal.SIGTERM, stop)
signal.signal(signal.SIGINT, stop)


def input_available():
    """Check if data is available on stdin."""
    data_available = select.select([sys.stdin], [], [], 0)
    return sys.stdin in data_available[0]

data = ""
while not STOP:
    if input_available():
        char = sys.stdin.buffer.read(1)
        try:
            char = char.decode('utf-8')
            data += char
        except UnicodeDecodeError:
            char = None
            print("skipping char")
        if char == '\n':
            s = f"Got line {data}"
            print(s)
            log.debug(s)
            data = ""
        sys.stdout.flush()

编辑 从“查尔斯·达菲”那封信里,我和斯特拉斯一起查了一下。似乎子进程中的程序正在阻塞它的read调用? 这是strace输出的最后一位:

write(1, "Writing\n", 8Writing
)                = 8
write(4, "blablabla\n", 10)             = 10
select(0, NULL, NULL, NULL, {tv_sec=2, tv_usec=0}) = 0 (Timeout)
read(5,

Tags: pyimportdatareadlinesignal进程stdinstdout