Tengo un contenedor de ctypes para una biblioteca. Desafortunadamente, esta biblioteca no es 100% confiable (segfaults ocasionales, etc.). Debido a cómo se usa, quiero que el contenedor sea razonablemente resistente al bloqueo de la biblioteca.

La mejor manera de hacer esto parece ser bifurcar un proceso y enviar los resultados del niño. Me gustaría hacer algo en este sentido:

r, w = os.pipe()
pid = os.fork()

if pid == 0:
    # child
    result = ctypes_fn()
    os.write(w, pickle.dumps(result))
    os.close(w)
else:
    # parent
    os.waitpid(pid, 0)
    result = os.read(r, 524288) # can be this big
    os.close(r)

    return pickle.loads(result)

Sin embargo, esto no funciona del todo. El proceso bifurcado se cuelga en la escritura. ¿Estoy tratando de enviar demasiado de una vez? ¿Existe una solución más simple para este problema?

3
iconoplast 16 abr. 2009 a las 20:47

3 respuestas

La mejor respuesta

Probablemente esté tratando de escribir más datos de los que caben en la tubería, por lo que se está bloqueando hasta que alguien venga y lea parte de esa información. Eso nunca sucederá, porque el único lector es el proceso padre, que parece haber escrito para esperar hasta que el hijo termine antes de leer algo. Esto es lo que llamamos un punto muerto .

Puede considerar sacar esa llamada os.waitpid y ver qué sucede. Otra opción sería ver si os.pipe tiene algún método que le otorgue un búfer más grande (no conozco su entorno lo suficiente como para decir).

4
T.E.D. 16 abr. 2009 a las 17:45

El problema básico es que hay un límite de 64kB en la tubería. Algunas posibles soluciones, desde lo simple a lo complejo:

  1. Enviar menos datos. zlib.compress podría ayudar a superar el límite.
  2. Almacene los datos reales en otro lugar (archivo, mmap, memcache), solo usando la tubería para enviar información de control.
  3. Continúe usando la tubería, pero fragmente la salida. Use dos conjuntos de tuberías para que los procesos puedan comunicarse entre sí y sincronizar su comunicación. El código es más complejo, pero por lo demás es muy efectivo.
2
iconoplast 16 abr. 2009 a las 19:43

Una solución al punto muerto que mencionó ted.dennison es el siguiente pseudocódigo:

#parent
while waitpid(pid, WNOHANG) == (0, 0):
    result = os.read(r, 1024)
    #sleep for a short time
#at this point the child process has ended 
#and you need the last bit of data from the pipe
result = os.read(r, 1024)
os.close(r)

Waitpid con la opción WNOHANG hace que waitpid regrese inmediatamente cuando el proceso secundario aún no se haya cerrado. En este caso, devuelve (0,0). Deberá asegurarse de no sobrescribir la variable de resultado cada vez a través del bucle como lo hace el código anterior.

0
Jeff 16 abr. 2009 a las 19:15