Обход буферизации вывода подпроцесса с помощью popen в C или Python - PullRequest
12 голосов
/ 11 сентября 2009

У меня есть общий вопрос о popen (и всех связанных функциях), применимый ко всем операционным системам, когда я пишу скрипт на python или некоторый код на c и запускаю полученный исполняемый файл из консоли (win или linux), я могу немедленно увидеть вывод из процесса. Однако, если я запускаю тот же исполняемый файл, что и разветвленный процесс с его stdout, перенаправленным в канал, выходные буферы куда-то, обычно до 4096 байтов, перед записью в канал, где родительский процесс может его прочитать.

Следующий скрипт Python сгенерирует выходные данные кусками по 1024 байта

import os, sys, time

if __name__ == "__main__":
     dye = '@'*1024
     for i in range (0,8):
        print dye
        time.sleep(1)

Следующий скрипт Python выполнит предыдущий скрипт и прочитает вывод, как только он попадет в канал, побайтово

import os, sys, subprocess, time, thread

if __name__ == "__main__":
    execArgs = ["c:\\python25\\python.exe", "C:\\Scripts\\PythonScratch\\byte_stream.py"]

    p = subprocess.Popen(execArgs, bufsize=0, stdout=subprocess.PIPE)
    while p.returncode == None:
        data = p.stdout.read(1)
        sys.stdout.write(data)
        p.poll()

Настройте путь для вашей операционной системы. При запуске в этой конфигурации выходные данные будут отображаться не порциями 1024, а порциями 4096, несмотря на то, что для размера буфера команды popen установлено значение 0 (в любом случае это значение по умолчанию). Может кто-нибудь сказать мне, как изменить это поведение ?, есть ли способ заставить операционную систему обрабатывать вывод от разветвленного процесса так же, как при запуске из консоли?, То есть просто передавать данные через без буферизации?

Ответы [ 2 ]

15 голосов
/ 11 сентября 2009

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

Если вы контролируете программу, которая выполняет написание, вы можете (как предлагает другой ответ) непрерывно очищать stdout или (более элегантно, если это возможно) пытаться заставить stdout быть небуферизованным, например, запустив Python с флагом командной строки -u:

-u     : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)
         see man page for details on internal buffering relating to '-u'

(то, что добавляет man-страница, это упоминание о stdin и проблемах с двоичным режимом [s]).

Если вы не можете или не хотите касаться программы, которая пишет, -u или что-то подобное в программе, которая только что читает, вряд ли поможет (буферизация, которая важнее всего, это та, что происходит в stdout автора) , а не тот, что на читательском stdin). Альтернатива состоит в том, чтобы заставить писателя поверить, что он пишет в терминал (хотя на самом деле он пишет в другую программу!) Через стандартный библиотечный модуль pty или стороннюю высокоуровневую pexpect модуль (или, для Windows, его порт wexpect ).

1 голос
/ 11 сентября 2009

Это верно и применимо как к Windows, так и к Linux (и, возможно, к другим системам), с popen() и fopen(). Если вы хотите, чтобы выходной буфер отправлялся раньше 4096 байт, используйте fflush() (на C) или sys.stdout.flush() (Python).

...