Обход буферизации вывода подпроцесса с помощью popen в C или Python
У меня есть общий вопрос о popen (и всех связанных функциях), применимый ко всем операционным системам, когда я пишу python script или некоторый c-код и запускаю полученный исполняемый файл из консоли (win или linux), я может сразу увидеть результат процесса. Тем не менее, если я запускаю тот же исполняемый файл, что и разветвленный процесс, а его stdout перенаправляется в канал, выходные буферы где-то, обычно до 4096 байт, прежде чем он будет записан в канал, где родительский процесс может его прочитать.
Следующий python script будет генерировать вывод в кусках 1024 байта
import os, sys, time
if __name__ == "__main__":
dye = '@'*1024
for i in range (0,8):
print dye
time.sleep(1)
Следующий python script выполнит предыдущий script и прочитает вывод, как только он поступит в трубу, байт по байт
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 (что по умолчанию все равно). Может ли кто-нибудь сказать мне, как изменить это поведение?, можно ли каким-либо образом заставить операционную систему обрабатывать вывод из разветвленного процесса так же, как когда он запускается с консоли?, т.е. Просто кормить данные через без буферизации?
Ответы
Ответ 1
В общем, стандартная библиотека времени C (которая работает от имени почти каждой программы в каждой системе, более или менее;-) определяет, является ли stdout терминалом или нет; если нет, он буферизует вывод (который может быть огромным выигрышем в эффективности, по сравнению с небуферизованным выходом).
Если вы контролируете программу, выполняющую запись, вы можете (как еще один ответ предлагал) постоянно поддерживать поток flush или (более элегантно, если это возможно), пытаться заставить 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).
Ответ 2
Это правильно и относится как к Windows, так и к Linux (и, возможно, к другим системам), с popen()
и fopen()
. Если вы хотите, чтобы выходной буфер отправлялся до 4096 байт, используйте fflush()
(на C) или sys.stdout.flush()
(Python).