python c subprocess hang

El subproceso del programa Python C se cuelga en "for line in iter"



subprocess hang (3)

Su programa no está bloqueado, simplemente funciona muy lentamente. Su programa está utilizando salida en búfer; los datos "2000/n" no se escriben en stdout inmediatamente, pero eventualmente lo harán. En su caso, podría tomar BUFSIZ/strlen("2000/n") segundos (probablemente 1638 segundos) para completar.

Después de esta línea:

printf("2000/n");

añadir

fflush(stdout);

Ok, estoy intentando ejecutar un programa C desde un script de Python. Actualmente estoy usando un programa C de prueba:

#include <stdio.h> int main() { while (1) { printf("2000/n"); sleep(1); } return 0; }

Para simular el programa que voy a utilizar, que toma lecturas de un sensor constantemente. Luego trato de leer el resultado (en este caso "2000" ) del programa C con subproceso en python:

#!usr/bin/python import subprocess process = subprocess.Popen("./main", stdout=subprocess.PIPE) while True: for line in iter(process.stdout.readline, ''''): print line,

Pero esto no está funcionando. Desde el uso de declaraciones de impresión, ejecuta la línea .Popen luego espera la for line in iter(process.stdout.readline, ''''): hasta que presione Ctrl-C.

¿Por qué es esto? Esto es exactamente lo que la mayoría de los ejemplos que he visto tienen como código, y aún así no lee el archivo.

Editar:

¿Hay alguna manera de hacerlo funcionar solo cuando hay algo que leer?


Ver los documentos de readline .

Tu codigo:

process.stdout.readline

Está esperando EOF o una nueva línea.

No puedo decir lo que en última instancia está tratando de hacer, pero agregando una nueva línea a su printf, por ejemplo, printf("2000/n"); , al menos debería comenzar.


Es un problema de bloqueo de bloques.

Lo que sigue es una extensión para su versión de caso de mi respuesta a Python: leer la entrada de transmisión de la pregunta subprocess.communicate () .

Solucione el búfer stdout en el programa C directamente

stdio programas basados ​​en stdio , por regla general, se almacenan en búfer de línea si se ejecutan de forma interactiva en un terminal y bloquean en búfer cuando su stdout se redirige a un conducto. En este último caso, no verá líneas nuevas hasta que el búfer se desborde o se vacíe.

Para evitar llamar a fflush() después de cada llamada a printf() , puede forzar la salida del buffer de línea invocando un programa en C desde el principio:

setvbuf(stdout, (char *) NULL, _IOLBF, 0); /* make line buffered stdout */

Tan pronto como se imprime una nueva línea, el búfer se vacía en este caso.

O arreglarlo sin modificar la fuente del programa C

Existe la utilidad stdbuf que le permite cambiar el tipo de almacenamiento en búfer sin modificar el código fuente, por ejemplo:

from subprocess import Popen, PIPE process = Popen(["stdbuf", "-oL", "./main"], stdout=PIPE, bufsize=1) for line in iter(process.stdout.readline, b''''): print line, process.communicate() # close process'' stream, wait for it to exit

También hay otras utilidades disponibles, consulte Desactivar el almacenamiento en búfer en la tubería .

O use pseudo-TTY

Para engañar al subproceso y pexpect pensar que se está ejecutando de manera interactiva, puede usar el módulo pexpect o sus análogos, para ejemplos de código que usan módulos pexpect y pty , vea que las pexpect () de subproceso de Python se cuelgan . Aquí hay una variación del ejemplo que se proporciona allí (debería funcionar en Linux):

#!/usr/bin/env python import os import pty import sys from select import select from subprocess import Popen, STDOUT master_fd, slave_fd = pty.openpty() # provide tty to enable line buffering process = Popen("./main", stdin=slave_fd, stdout=slave_fd, stderr=STDOUT, bufsize=0, close_fds=True) timeout = .1 # ugly but otherwise `select` blocks on process'' exit # code is similar to _copy() from pty.py with os.fdopen(master_fd, ''r+b'', 0) as master: input_fds = [master, sys.stdin] while True: fds = select(input_fds, [], [], timeout)[0] if master in fds: # subprocess'' output is ready data = os.read(master_fd, 512) # <-- doesn''t block, may return less if not data: # EOF input_fds.remove(master) else: os.write(sys.stdout.fileno(), data) # copy to our stdout if sys.stdin in fds: # got user input data = os.read(sys.stdin.fileno(), 512) if not data: input_fds.remove(sys.stdin) else: master.write(data) # copy it to subprocess'' stdin if not fds: # timeout in select() if process.poll() is not None: # subprocess ended # and no output is buffered <-- timeout + dead subprocess assert not select([master], [], [], 0)[0] # race is possible os.close(slave_fd) # subproces don''t need it anymore break rc = process.wait() print("subprocess exited with status %d" % rc)

O use pty través de pexpect

pexpect envuelve el manejo pty en una interfaz de nivel superior :

#!/usr/bin/env python import pexpect child = pexpect.spawn("/.main") for line in child: print line, child.close()

P: ¿Por qué no usar una tubería (popen ())? explica por qué pseudo-TTY es útil.