subprocess.call() 和 subprocess.Popen() 之间有什么区别使前者的 PIPE 不那么安全?

What difference between subprocess.call() and subprocess.Popen() makes PIPE less secure for the former?

我已经查看了它们的文档。

此问题是由 J.F 提示的。此处的评论:Retrieving the output of subprocess.call()

subprocess.call() 的当前 Python 文档说明了以下关于将 PIPE 用于 subprocess.call() 的内容:

Note Do not use stdout=PIPE or stderr=PIPE with this function. The child process will block if it generates enough output to a pipe to fill up the OS pipe buffer as the pipes are not being read from.

Python 2.7 subprocess.call():

Note Do not use stdout=PIPE or stderr=PIPE with this function as that can deadlock based on the child process output volume. Use Popen with the communicate() method when you need pipes.

Python 2.6 不包含此类警告。

此外,subprocess.call() and subprocess.check_call() 似乎无法访问其输出,除了使用 stdout=PIPE with communicate() 之外:

https://docs.python.org/2.6/library/subprocess.html#convenience-functions

Note that if you want to send data to the process’s stdin, you need to create the Popen object with stdin=PIPE. Similarly, to get anything other than None in the result tuple, you need to give stdout=PIPE and/or stderr=PIPE too.

https://docs.python.org/2.6/library/subprocess.html#subprocess.Popen.communicate

subprocess.call()subprocess.Popen() 之间有什么区别使 PIPE 对于 subprocess.call() 来说更不安全?

更具体: 为什么 subprocess.call() "deadlock based on the child process output volume." 而不是 Popen()

callPopen 都提供了访问命令输出的方法:

  • 对于 Popen,您可以使用 communicate 或向 stdout=... 参数提供文件描述符或文件对象。
  • 对于 call,您唯一的选择是将文件描述符或文件对象传递给 stdout=... 参数(您不能将 communicate 用于此参数)。

现在,stdout=PIPEcall 一起使用时不安全的原因是因为 call 在子进程完成之前不会 return,这意味着所有在那一刻之前,输出必须驻留在内存中,如果输出量太多,则会填满 OS 管道缓冲区。

可以验证以上信息的参考资料如下:

  1. 根据 thiscallPopen 的参数相同:

The arguments shown above are merely the most common ones, described below in Frequently Used Arguments (hence the slightly odd notation in the abbreviated signature). The full function signature is the same as that of the Popen constructor - this functions passes all supplied arguments directly through to that interface.

  1. 根据 thisstdout 参数的可能值为:

Valid values are PIPE, an existing file descriptor (a positive integer), an existing file object, and None. PIPE indicates that a new pipe to the child should be created

call() is just Popen().wait() (± error handling).

您不应该将 stdout=PIPEcall() 一起使用,因为它不会从管道读取,因此子进程将在它填满后立即挂起相应的 OS 管道缓冲区。这是一张显示数据如何在 command1 | command2 shell 管道中流动的图片:

你的 Python 版本是什么并不重要——管道缓冲区(看图片)在你的 Python 进程之外。 Python 3 不使用 C stdio 但它只影响内部缓冲。当内部缓冲区被刷新时,数据进入管道。如果 command2(您的父 Python 程序)没有从管道读取,那么 command1(子进程,例如,由 call() 启动)将在管道缓冲区后立即挂起已满(pipe_size = fcntl(p.stdout, F_GETPIPE_SZ) ~65K 在我的 Linux 盒子上(最大值是 /proc/sys/fs/pipe-max-size ~1M))。

如果你稍后从管道读取,你可以使用stdout=PIPE,例如,使用Popen.communicate()方法。你也可以 read from process.stdout (the file object that represents the pipe) directly.