Программа Python с использованием os.pipe и os.fork()
Мне недавно нужно было написать script, который выполняет os.fork(), чтобы разделить на два процесса. Детский процесс становится серверным процессом и передает данные обратно в родительский процесс, используя канал, созданный с помощью os.pipe(). Ребенок закрывает конец 'r'
трубы, и родительский элемент закрывает конец 'w'
конца трубы, как обычно. Я конвертирую возвращаемые данные из pipe() в файловые объекты с помощью os.fdopen.
Проблема, с которой я столкнулась, заключается в следующем: процесс успешно вилки, и ребенок становится сервером. Все отлично работает, и ребенок покорно записывает данные в открытый конец 'w'
. К сожалению, у родительского конца трубы есть две странные вещи:
A) Он блокируется при операции read()
на конце 'r'
канала.
Во-вторых, он не может прочитать данные, которые были помещены в трубу, если конец 'w'
полностью не закрыт.
Я сразу же подумал, что проблема с буферизацией и добавлены вызовы pipe.flush(), но это не помогло.
Может кто-нибудь пролить свет на то, почему данные не появляются до тех пор, пока конец записи полностью не закрыт? И существует ли стратегия, чтобы вызов read()
не блокировался?
Это моя первая программа на Python, которая разветвляла или использовала трубы, поэтому простите меня, если я совершил простую ошибку.
Ответы
Ответ 1
Используете ли вы read() без указания размера или обрабатываете канал как итератор (for line in f
)? Если это так, вероятно, источник вашей проблемы - read() определяется для чтения до конца файла перед возвратом, а не просто читать то, что доступно для чтения. Это будет означать, что он будет блокироваться до тех пор, пока ребенок не назовет close().
В примере кода, связанного с этим, это нормально - родитель действует блокировочно и просто использует ребенка для целей изоляции. Если вы хотите продолжить, используйте либо неблокирующий IO, как в коде, который вы отправили (но будьте готовы к полузаполнению данных), либо прочитайте фрагменты (например, r.read(размер) или r.readline()), который будет блокироваться только до тех пор, пока не будет прочитан конкретный размер/строка. (вам все равно нужно вызвать флеш для ребенка)
Похоже, что обработка канала в качестве итератора использует еще один буфер, поскольку "for line in r:
" может не дать вам то, что вы хотите, если вам нужно, чтобы каждая строка была немедленно использована. Возможно, это можно отключить, но просто указать 0 для размера буфера в fdopen не представляется достаточным.
Вот пример кода, который должен работать:
import os, sys, time
r,w=os.pipe()
r,w=os.fdopen(r,'r',0), os.fdopen(w,'w',0)
pid = os.fork()
if pid: # Parent
w.close()
while 1:
data=r.readline()
if not data: break
print "parent read: " + data.strip()
else: # Child
r.close()
for i in range(10):
print >>w, "line %s" % i
w.flush()
time.sleep(1)
Ответ 2
Используя
fcntl.fcntl(readPipe, fcntl.F_SETFL, os.O_NONBLOCK)
Перед вызовом read() решены обе проблемы. Вызов read() больше не блокируется, и данные появляются после только флеша() на конце записи.
Ответ 3
Я вижу, что вы решили проблему блокировки ввода/вывода и буферизации.
Примечание, если вы решите попробовать другой подход: подпроцесс является эквивалентом/заменой fork/exec idiom. Похоже, что это не то, что вы делаете: у вас есть только вилка (а не exec) и обмен данными между двумя процессами - в этом случае multiprocessing
модуль (в Python 2.6+) будет лучше подходит.
Ответ 4
Вот несколько примеров кода для этого.
Ответ 5
"родительская" и "дочерняя" части fork в приложении Python глупо. Это наследие из 16-битных unix-дней. Это аффект с дня, когда fork/exec и exec были важными вещами, чтобы максимально использовать крошечный процессор.
Разделите свой код Python на две отдельные части: родительский и дочерний.
Родительская часть должна использовать subprocess для запуска дочерней части.
Вилка и exec могут произойти где-то там, но вам не нужно заботиться.