Какая разница между subprocess.call() и subprocess.Popen() делает PIPE менее безопасным для первого?

Я посмотрел документацию для них обоих.

Этот вопрос вызван комментарием J.F. здесь: Получение вывода subprocess.call()

В текущей документации Python для subprocess.call() говорится следующее об использовании PIPE для subprocess.call():

Примечание. Не используйте stdout=PIPE или stderr=PIPE с помощью этой функции. Детский процесс будет блокироваться, если он генерирует достаточный вывод в канал для заполнения буфера для буфера OS, поскольку каналы не читаются.

Python 2.7 subprocess.call():

Примечание. Не используйте stdout=PIPE или stderr=PIPE с этой функцией, так как это может быть блокировка на основе выходного тома дочернего процесса. Используйте Popen с методом communication(), когда вам нужны трубы.

Python 2.6 не содержит таких предупреждений.

Кроме того, subprocess.call() и subprocess.check_call(), похоже, не имеют доступа к их выходным данным, за исключением использования stdout = PIPE с сообщением():

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

Обратите внимание, что если вы хотите отправить данные процессам stdin, вам нужно создать объект Popen с помощью stdin=PIPE. Точно так же, чтобы получить ничего, кроме None в кортеже результата, вам нужно также дать stdout=PIPE и/или stderr=PIPE.

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

Какая разница между subprocess.call() и subprocess.Popen() делает PIPE менее безопасным для subprocess.call()?

Конкретнее: Зачем нужен тупик subprocess.call() на основе выходного тома дочернего процесса. ", а не Popen()?

Ответ 1

call() - это просто Popen().wait() (± обработка ошибок).

Вы не должны использовать stdout=PIPE с call(), потому что он не читает из канала, и поэтому дочерний процесс зависает, как только он заполняет соответствующий буфер буфера операционной системы. Вот изображение, показывающее, как данные передаются в конвейере command1 | command2:

pipe/stdio buffers

Не имеет значения, какова ваша версия 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(). Вы также можете читать с process.stdout (файловый объект, представляющий канал).

Ответ 2

Оба call и Popen предоставляют средства для доступа к выходу вашей команды:

  • С помощью Popen вы можете использовать communicate или предоставить файловый дескриптор или файл-объект параметру stdout=....
  • С call ваш единственный вариант - передать файловый дескриптор или файл-объект в параметр stdout=... (вы не можете использовать communicate с этим).

Теперь причина, по которой stdout=PIPE является небезопасной при использовании с call, заключается в том, что call не возвращается до завершения подпроцесса, это означает, что весь вывод должен был находиться в памяти до этого момента и если объем вывода слишком большой, то это будет заполнять буфер труб OS.

Ссылки, в которых вы можете проверить приведенную выше информацию, следующие:

  • В соответствии с this параметры для call и Popen совпадают:

Приведенные выше аргументы являются лишь наиболее распространенными из них ниже в часто используемых аргументах (следовательно, слегка нечетные обозначения в сокращенная подпись). Полная подпись функции такая же, как и функции конструктора Popen - эти функции передают все поставляемые аргументы непосредственно к этому интерфейсу.

  1. В соответствии с this возможные значения для параметра stdout:

Допустимыми значениями являются PIPE, существующий файловый дескриптор (положительный целое), существующий файловый объект и None. PIPE указывает, что новый должен быть создан канал для ребенка