Программа на Python, использующая проблемы os.pipe и os.fork () - PullRequest
10 голосов
/ 16 мая 2009

Мне недавно нужно было написать скрипт, который выполняет os.fork () , чтобы разделить на два процесса. Дочерний процесс становится серверным процессом и передает данные обратно в родительский процесс, используя канал, созданный с помощью os.pipe () . Потомок закрывает конец 'r' канала, а родитель закрывает конец 'w' канала, как обычно. Я преобразую возвращаемые данные из pipe () в файловые объекты с os.fdopen .

Проблема, с которой я столкнулся, заключается в следующем: процесс успешно разветвляется, и ребенок становится сервером. Все отлично работает, и ребенок покорно записывает данные в открытый конец канала 'w'. К сожалению, родительский конец канала делает две странные вещи:
A) Он блокирует операцию read() на конце 'r' трубы.
Во-вторых, он не может прочитать любые данные, которые были помещены в канал, если конец 'w' не закрыт полностью.

Я сразу подумал, что проблема с буферизацией, и добавил pipe.flush () , но это не помогло.

Может кто-нибудь пролить свет на то, почему данные не появляются, пока конец записи не будет полностью закрыт? И есть ли стратегия сделать вызов read() неблокирующим?

Это моя первая программа на Python, которая разветвляет или использует каналы, так что простите меня, если я допустил простую ошибку.

Ответы [ 4 ]

12 голосов
/ 16 мая 2009

Используете ли вы read () без указания размера или рассматриваете канал как итератор (for line in f)? Если это так, то это, вероятно, источник вашей проблемы - read () определено для чтения до конца файла перед возвратом, а не просто для чтения того, что доступно для чтения. Это будет означать, что он будет блокироваться, пока ребенок не вызовет close ().

В примере кода, с которым связано, все в порядке - родительский элемент действует блокирующим образом и просто использует дочерний элемент в целях изоляции. Если вы хотите продолжить, то используйте неблокируемый ввод-вывод, как в опубликованном вами коде (но будьте готовы к обработке неполных данных), или читайте кусками (например, r.read (size) или 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)
5 голосов
/ 16 мая 2009

Использование

fcntl.fcntl(readPipe, fcntl.F_SETFL, os.O_NONBLOCK)

Перед вызовом read () решил обе проблемы. Вызов read () больше не блокируется, и данные появляются только после очистки () в конце записи.

4 голосов
/ 16 мая 2009

Я вижу, вы решили проблему блокировки ввода-вывода и буферизации.

Примечание, если вы решите попробовать другой подход: подпроцесс является эквивалентом / заменой идиомы fork / exec. Похоже, что это не то, что вы делаете: у вас есть только вилка (не exec) и обмен данными между двумя процессами - в этом случае модуль multiprocessing (в Python 2.6+) будет лучше подойдет.

0 голосов
/ 16 мая 2009

«родительская» или «дочерняя» часть fork в приложении Python глупа. Это наследие 16-битных дней Unix. Это впечатляет от того дня, когда fork / exec и exec были важными вещами, чтобы максимально использовать крошечный процессор.

Разбейте ваш код Python на две отдельные части: родительскую и дочернюю.

В родительской части должен использоваться подпроцесс для запуска дочерней части.

Где-то там может произойти форк и exec, но вам не нужно об этом заботиться.

...