2009-05-16 19 views
10

Recientemente he necesitado escribir un script que realiza un os.fork() para dividirlo en dos procesos. El proceso secundario se convierte en un proceso de servidor y transfiere los datos al proceso principal utilizando un conducto creado con os.pipe(). El hijo cierra el extremo 'r' de la tubería y el padre cierra el extremo 'w' de la tubería, como de costumbre. Convierto los retornos de pipe() en objetos de archivo con os.fdopen.Programa de Python que usa os.pipe y os.fork() problema

El problema que estoy teniendo es esto: el proceso se bifurca con éxito, y el niño se convierte en un servidor. Todo funciona muy bien y el niño escribe diligentemente los datos en el extremo abierto de la tubería 'w'. Lamentablemente, el extremo inicial de la tubería hace dos cosas extrañas:
A) Bloquea en la operación read() en el extremo 'r' de la tubería.
En segundo lugar, no puede leer los datos que se colocaron en el conducto a menos que el extremo 'w' esté completamente cerrado.

Inmediatamente pensé que el problema era el almacenamiento en búfer y agregué pipe.flush() llamadas, pero estas no ayudaron.

¿Alguien puede arrojar algo de luz sobre por qué los datos no aparecen hasta que el final de la escritura está completamente cerrado? ¿Y existe una estrategia para hacer que la llamada read() no bloquee?

Este es mi primer programa de Python que bifurcó o usó tuberías, así que perdónenme si cometí un simple error.

+1

Por qué no está usando el módulo subproceso? –

+0

Pensé que el módulo de subproceso era para invocar un comando. Estoy bifurcando con dos ramas de código, una para el niño y otra para el padre. – Paradox

+0

¿Podría publicar algún código? –

Respuesta

11

¿Está utilizando read() sin especificar un tamaño o tratando el conducto como un iterador (for line in f)?Si es así, esa es probablemente la fuente de su problema: read() se define para leer hasta el final del archivo antes de regresar, en lugar de solo leer lo que está disponible para leer. Eso significará que bloqueará hasta que el niño llame close().

En el código de ejemplo vinculado a, esto está bien: el elemento principal actúa de forma bloqueante y solo usa el elemento secundario para fines de aislamiento. Si desea continuar, utilice E/S no bloqueante como en el código que publicó (pero prepárese para tratar la mitad de los datos completos) o lea en fragmentos (por ejemplo, r.read (tamaño) o r.readline()) que bloqueará solo hasta que se haya leído un tamaño/línea específico. (aún necesitará llamar a color en el niño)

Parece que tratar el conducto ya que un iterador también está usando otro buffer, para "for line in r:" puede que no le proporcione lo que usted necesita si necesita cada línea para ser consumido inmediatamente Puede ser posible desactivar esto, pero simplemente especificar 0 para el tamaño del búfer en fdopen no parece suficiente.

Heres un código de ejemplo que debería funcionar:

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

¡Agradable! también, considere reemplazar 'while 1: ...' con 'para los datos en iter (r.readline," "):' – mdaoust

-9

La parte "principal" frente a "secundaria" de fork en una aplicación de Python es tonta. Es un legado de los días de Unix de 16 bits. Es una afectación de un día en que fork/exec y exec eran cosas importantes para sacar el máximo provecho de un pequeño y pequeño procesador.

Divida su código de Python en dos partes separadas: principal e hijo.

La parte principal debe usar subprocess para ejecutar la parte secundaria.

Un tenedor y un ejecutivo pueden aparecer en algún lugar, pero no es necesario que se preocupe.

+0

La cosa "padre" frente a "hijo" es parte de la semántica esencial de iniciar un subproceso. Uno es el subproceso y el otro no. –

+0

Aunque es cierto que el fork crea un padre y un hijo, no es esencial para crear un subproceso. Abrir VMS no funciona de esa manera. El módulo de subproceso es mucho más simple que esta malarkey de horquilla. –

+2

No piense en 'fork()' como el equivalente de 'CreateProcess()' en Windows, o el equivalente en VMS, que es básicamente lo que el módulo de subproceso se modela después. 'fork()' es mucho más parecido a comenzar un nuevo hilo, excepto que el hilo pasa a tener un espacio de proceso diferente (y por lo tanto necesita comunicarse con él a través de tuberías en lugar de la memoria compartida). Usando el módulo 'subprocess' necesita ejecutar la inicialización del proceso (como analizar archivos de configuración o argumentos de línea de comandos) dos veces, mientras que con' fork() 'no lo hace. Como tal, 'fork()' puede ser mucho más eficiente. –

2

Here's un código de ejemplo para hacer esto.

+0

Este es el sitio en el que basé mi código originalmente. Gracias – Paradox

5

Usando

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

Antes de invocar la lectura() resuelto ambos problemas. La llamada a la función de lectura() ya no está bloqueando y los datos aparecen después de solo una descarga() en el extremo de escritura.

4

Veo que ha resuelto el problema de bloqueo de E/S y almacenamiento en búfer.

Una nota si decide probar un enfoque diferente: subproceso es el equivalente/un reemplazo para la expresión fork/exec. Parece que no es eso lo que estás haciendo: solo tienes un tenedor (no un ejecutivo) e intercambias datos entre los dos procesos; en este caso, el módulo multiprocessing (en Python 2.6+) sería una mejor opción.

+0

Este módulo se ve muy interesante. Gracias, lo comprobaré. – Paradox

+1

+1 para mencionar la diferencia entre 'fork()' (lo que el OP intenta hacer aquí) y la expresión 'fork' /' exec' encapsulada por el módulo de subproceso, que es algo completamente diferente. –

Cuestiones relacionadas