Python subprocess.call()和subprocess.Popen()之间有什么区别使得前者的管道不那么安全?
我已经看了他们两个的文档 这个问题是由J.F.在这里的评论引发的: 当前的Python文档说明了如何将Python subprocess.call()和subprocess.Popen()之间有什么区别使得前者的管道不那么安全?,python,python-2.7,subprocess,popen,python-2.6,Python,Python 2.7,Subprocess,Popen,Python 2.6,我已经看了他们两个的文档 这个问题是由J.F.在这里的评论引发的: 当前的Python文档说明了如何将管道用于子流程.call(): 注意不要将stdout=PIPE或stderr=PIPE与此功能一起使用。如果子进程生成足够多的输出到管道,以填充操作系统管道缓冲区,则子进程将阻塞,因为没有从中读取管道 Python 2.7: 注意:不要将stdout=PIPE或stderr=PIPE与此函数一起使用,因为这可能会基于子进程输出卷死锁。需要管道时,请将Popen与communicate()方法一
管道用于子流程.call()
:
注意不要将stdout=PIPE
或stderr=PIPE
与此功能一起使用。如果子进程生成足够多的输出到管道,以填充操作系统管道缓冲区,则子进程将阻塞,因为没有从中读取管道
Python 2.7:
注意:不要将stdout=PIPE
或stderr=PIPE
与此函数一起使用,因为这可能会基于子进程输出卷死锁。需要管道时,请将Popen与communicate()方法一起使用
Python2.6不包含此类警告
此外,除了将stdout=PIPE与communicate()一起使用外,用户似乎没有访问其输出的方法:
请注意,如果要将数据发送到进程的stdin
,则需要使用stdin=PIPE
创建Popen
对象。类似地,要在结果元组中获得除None之外的任何内容,还需要给出stdout=PIPE
和/或stderr=PIPE
subprocess.call()
和subprocess.Popen()
之间有什么区别使得subprocess.call()的管道不那么安全
更具体:为什么subprocess.call()
是“基于子进程输出卷的死锁”,而不是Popen()
call
和Popen
都提供了访问命令输出的方法:
- 使用
Popen
可以使用communicate
或为stdout=…
参数提供文件描述符或文件对象
- 使用
call
时,您唯一的选择是将文件描述符或文件对象传递给stdout=…
参数(您不能使用与此参数进行通信)
现在,当与call
一起使用时,stdout=PIPE
不安全的原因是call
在子进程完成之前不会返回,这意味着所有的输出都必须驻留在内存中,如果输出量太多,那么就会填满操作系统管道的缓冲区
您可以验证上述信息的参考资料如下:
根据call
和Popen
的参数相同:
上面所示的论点仅仅是最常见的论点
在下面的常用参数中(因此在
缩写签名)。完整函数签名与
Popen构造函数的函数-此函数传递所有提供的
参数直接传递到该接口
根据stdout
参数的可能值:
有效值是管道,一个现有的文件描述符(正数)
整数)、现有文件对象和无。管道表示一个新的
应创建到子级的管道
您不应该将stdout=PIPE
与call()
一起使用,因为它不从管道中读取,因此子进程在填充相应的OS管道缓冲区后将立即挂起。下面的图片显示了数据如何在command1 | command2
shell管道中流动:
不管您的Python版本是什么——管道缓冲区(请看图片)在Python进程之外。Python3不使用C stdio,但它只影响内部缓冲。刷新内部缓冲区时,数据进入管道。如果command2
(您的父Python程序)没有从管道中读取数据,那么command1
(子进程,例如,由call()
)启动的进程将在管道缓冲区满时挂起(在我的Linux机器上为~65K(最大值为/proc/sys/fs/pipe max size
~1M))
如果以后使用管道读取,例如使用Popen.communicate()
方法,则可以使用stdout=PIPE
。你也可以。//,接下来的问题:Python 2和Python 3之间的这种差异是如何变化的?为什么你总是从/
开始呢?@chepner:嗯,按钮上说的是“添加注释”…:)/,Meta的后续问题:为什么这么少的人有幽默感?”也许,我是,但Python注释符号是“代码”,< < /代码>,不像C++、PHP和JavaScript。/看起来,“管”隐喻很好用。我不知道我能这么直截了当地认为可能有一个塞满了的管子。我想知道为什么内核的管道缓冲区不能自动刷新自己,或者为什么call()
不能刷新缓冲区。为了延伸“管道”的比喻,如果管道安装在可能无法从管道中取出的地方,为什么不留下一个压力激活连接(电磁阀?)到排水槽?(“刷新缓冲区!使代码更难!”-可能的口号?/),我可以猜到为什么一些系统程序员称为“水管工”。这一切都在管道中。有一种说法是“自动刷新管道缓冲区”;它被称为:“drop data”,实现为stdout=DEVNULL
(您可以通过call()
)安全地使用它。)/,啊,但是以后,人们就不能再选择使用communicate()
与call()
的输出或check\u call()
的输出,对吗?@Nathanbassanese:call()
返回子级的退出状态——它是一个普通整数(POSIX上为8位,Windows上为32位);您不能用它调用communicate()
。无论如何,子进程已经死了,甚至它的PID可能已经被其他进程重用