2009-05-16 2 views
10

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

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

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

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

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

+1

Почему вы не используете модуль подпроцесса? –

+0

Я думал, что модуль подпроцесса предназначен для вызова команды. Я использую две ветви кода, одну для ребенка и одну для родителя. – Paradox

+0

Не могли бы вы вывести код? –

ответ

11

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

В примере кода, связанного с этим, это нормально - родитель действует блокировочно и просто использует дочерний объект для целей изоляции. Если вы хотите продолжить, используйте либо неблокирующий IO, как в коде, который вы отправили (но будьте готовы к полузаполнению данных), либо прочитайте фрагменты (например, r.read (размер) или r.readline()), который будет блокироваться только до тех пор, пока не будет прочитан конкретный размер/строка. (вам все равно нужно будет навести флажок на ребенка)

Похоже, что обработка трубы в качестве итератора использует дополнительный буфер, а для «for line in r:» может не дать вам то, что вы хотите, если вам нужна каждая строка для немедленного потребления. Возможно, это можно отключить, но просто указать 0 для размера буфера в fdopen не представляется достаточным.

Heres некоторые примеры кода, который должен работать:

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) 
+0

Ницца! также, подумайте о замене 'while 1: ...' на 'для данных в iter (r.readline," "):' – mdaoust

-9

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

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

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

Вилка и исполнитель могут произойти где-то там, но вам не нужно заботиться.

+0

«Родительская» и «дочерняя» вещь является частью существенной семантики запуска подпроцесса. Один из них - подпроцесс, а другой - нет. –

+0

Хотя верно, что fork создает родительский элемент и дочерний элемент, для создания подпроцесса не обязательно. Открытый VMS не работает. Модуль подпроцесса намного проще, чем этот fork malarkey. –

+2

Не думайте о 'fork()' как эквиваленте 'CreateProcess()' в Windows или эквивалентном в VMS, что в основном относится к модели модуля подпроцесса. 'fork()' больше похож на запуск нового потока, за исключением того, что в потоке происходит другое пространство процесса (и поэтому вам нужно обмениваться данными с ним через каналы вместо общей памяти). Используя модуль 'subprocess', вам нужно дважды запускать инициализацию процесса (например, анализировать файлы конфигурации или аргументы командной строки), а с' fork() 'вы этого не делаете. Таким образом, 'fork()' может быть намного более эффективным. –

2

Here's некоторые примеры кода для этого.

+0

Это сайт, на котором я основывал свой код, изначально. Спасибо – Paradox

5

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

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

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

4

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

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

+0

Этот модуль выглядит очень интересным. Спасибо, я проверю это. – Paradox

+1

+1 для обозначения разницы между 'fork()' (что здесь пытается сделать OP) и '' fork''' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '', инкапсулированный модулем подпроцесса. –

Смежные вопросы