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.