Программа Python, использующая проблему os.pipe и os.fork()

StackOverflow https://stackoverflow.com/questions/871447

  •  22-08-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

Это было полезно?

Решение

Используете ли вы 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)

Другие советы

С использованием

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top