如何使用subprocess模块向后台进程管道写入数据?
Pythonic方式与后台进程持续交互并写入STDIN
问题根源
subprocess.communicate()是设计用于一次性向子进程发送数据、读取输出并等待进程终止的方法,调用后会关闭stdin管道且全程阻塞,完全不适合多次动态写入的场景——这就是你只看到一次写入效果的原因。
解决方案
采用更直接的管道操作+线程处理输出的Pythonic方案,核心思路:
- 直接操作子进程的
stdin对象写入数据,通过flush()确保数据立即发送 - 启动独立线程读取子进程输出,避免输出缓冲区满导致子进程阻塞
- 修正子进程的非阻塞读取逻辑,处理无数据时的异常
修正后的待执行程序(ex.py)
原代码的非阻塞读取逻辑存在异常未捕获的问题,调整后:
import fcntl, os, sys, time orig_fl = fcntl.fcntl(sys.stdin, fcntl.F_GETFL) fcntl.fcntl(sys.stdin, fcntl.F_SETFL, orig_fl | os.O_NONBLOCK) i = 0 while True: try: # 非阻塞读取所有可用数据,无数据时抛出BlockingIOError data = sys.stdin.read() if data: # 按行分割处理输入 for line in data.splitlines(): try: i = int(line) except ValueError: pass except BlockingIOError: # 无输入时跳过异常 pass print(i) time.sleep(1) i += 1
修改后的驱动程序(drive.py)
import subprocess import time from threading import Thread def read_subprocess_output(proc): # 线程函数:持续读取子进程输出并打印 for line in proc.stdout: print(line.strip()) # 启动子进程,配置管道和缓冲 proc = subprocess.Popen( ['/usr/bin/python3', './ex.py'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, # 合并stderr到stdout统一处理 bufsize=1, # 行缓冲模式 universal_newlines=True # 文本模式,自动处理编码 ) # 启动读取输出的守护线程 Thread(target=read_subprocess_output, args=(proc,), daemon=True).start() try: for i in range(100): new_base = 100 * (i + 10) print(f'drive.py setting new_base to {new_base}') # 写入数据并强制刷新缓冲区 proc.stdin.write(f'{new_base}\n') proc.stdin.flush() time.sleep(5) finally: # 清理资源 proc.stdin.close() proc.wait()
关键说明
- 避免阻塞写入:用
proc.stdin.write()替代communicate(),配合flush()确保数据立即发送到子进程,不会因缓冲区积压延迟。 - 输出线程的必要性:子进程的输出如果不被持续读取,当系统输出缓冲区填满时会暂停执行,直到缓冲区空闲——线程方案可以避免主进程被阻塞。
- 非阻塞输入处理:子进程中捕获
BlockingIOError,处理无输入数据的场景,避免程序崩溃。
内容的提问来源于stack exchange,提问作者Jug of Bugs
相关产品推荐
相关产品推荐

