python subprocess.Popen read阻塞问题解决

python subprocess.Popen read阻塞问题解决

背景

使用subprocess.Popen打开一个子进程,指定子进程的标准输入,标准输出为subprocess.PIPE,使用stdout.read()读取子进程的标准输出,当子进程没有输出时read会导致程序阻塞。

原因分析

指定标准输入,标准输出,标准错误为subprocess.PIPE后,实则是使用匿名管道和子进程进行通信。匿名管道有两个非常重要的特性:
1.当写端一直写,而读端不去读,管道被写满后,写端继续写就会阻塞;
2.当写端打开不写数据,管道为空时,读端去读就会阻塞。
我们遇到的问题就是由上面第二个原因造成的。

解决思路

  1. 使用Popen.communicate()
    在python的官方文档中提到为了避免管道缓冲被填满导致阻塞,可以使用Popen.communicate()来规避,并且可以传入timeout参数。
    缺点:communicate()后便会关闭输入管道,无法和子进程继续交互,并且不是实时获取子进程输出。
  2. 使用非阻塞方式进行读取
    网上查阅一番资料后,看到的使用非阻塞方式读取只在linux下有效。
  3. 从匿名管道本身入手
    查看了windows关于匿名管道的api后发现了PeekNamedPipe()这个函数
    使用该函数可以查看管道中是否有数据,当存在数据时再去进行读取,就可以避免由于管道没有数据造成的阻塞。
    PeekNamedPipe函数说明
    最终,步骤如下:
# 使用CreatePipe创建爱你匿名管道
# 
import _winapi
import os
import subprocess
import time

SIZE = 128

# 创建匿名管道
...
...
# 使管道可以被子进程继承
...
...
# 使用管道描述符创建startupinfo
...
...
# 开启subprocess进程,使用PeekNamedPipe、ReadFile、GetOverlappedResult获取数据
...
...
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
subprocess.Popen在读取子进程的标准输出时,如果子进程没有输出,使用stdout.read()会导致程序阻塞。 为了避免阻塞,可以采用两种方法。一种是在读取输出之前,将文件描述符设置为非阻塞模式,例如使用fcntl库的fcntl函数进行设置。具体实现可以参考下面的代码示例: ``` import select import os import subprocess import time import fcntl args = ['python','./fetch_file2.py',ip,path] proc = subprocess.Popen(args, stdout=subprocess.PIPE,stderr=subprocess.PIPE,close_fds=True) def non_block_read(output): # 避免阻塞 fd = output.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) try: return output.read() except: return "" while proc.poll() is None: #fetch中rsync结束。但是fetch没有结束(怀疑输出过大) 导致这里一直是None pass print proc.poll() # 杀死fetch进程 返回-9 print proc.stderr.read() #阻塞#方法1: #non_block_read(proc.stderr) #防止阻塞#方法2: select_rfds = [ proc.stdout, proc.stderr] (rfds, wfds, efds) = select.select(select_rfds, [],[]) if proc.stderr in rfds: #不存在。若select_rfds=[stderr],则阻塞在select上 len = proc.stderr.read(10) if len == 0: print "empty" else: print "proc.stderr" if proc.stdout in rfds: print "proc.stdout" ``` 通过设置文件描述符为非阻塞模式,可以避免阻塞问题,并且使用select.select来检查子进程的标准输出是否可读。 这样即可解决subprocess.Popen阻塞问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [对Python subprocess.Popen子进程管道阻塞详解](https://download.csdn.net/download/weixin_38661128/12866393)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [python subprocess.Popen read阻塞问题解决](https://blog.csdn.net/weixin_41582874/article/details/127939185)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [python subprocess阻塞](https://blog.csdn.net/weixin_30855761/article/details/99389411)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值