tutorial sirve que para examples ejemplos python linux subprocess wine

python - sirve - Rendimiento de subprocess.check_output vs subprocess.call



subprocess python windows (2)

Al leer los documentos, subprocess.call y subprocess.check_output son casos de uso de subprocess.Popen . Una pequeña diferencia es que check_output generará un error de Python si el subproceso devuelve un estado de salida distinto de cero. La mayor diferencia se enfatiza en el bit sobre check_output (mi énfasis):

La firma de la función completa es en gran medida la misma que la del constructor de Popen, excepto que no se permite la salida estándar, ya que se utiliza internamente . Todos los demás argumentos proporcionados se pasan directamente al constructor de Popen.

Entonces, ¿cómo es stdout "utilizado internamente"? Comparemos call y check_output :

llamada

def call(*popenargs, **kwargs): return Popen(*popenargs, **kwargs).wait()

check_output

def check_output(*popenargs, **kwargs): if ''stdout'' in kwargs: raise ValueError(''stdout argument not allowed, it will be overridden.'') process = Popen(stdout=PIPE, *popenargs, **kwargs) output, unused_err = process.communicate() retcode = process.poll() if retcode: cmd = kwargs.get("args") if cmd is None: cmd = popenargs[0] raise CalledProcessError(retcode, cmd, output=output) return output

comunicar

Ahora tenemos que mirar también a Popen.communicate . Al hacer esto, notamos que para una tubería, la communicate hace varias cosas que simplemente toman más tiempo que simplemente devolver Popen().wait() , como lo hace la call .

Por un lado, communicate procesos stdout=PIPE si configura shell=True o no. Claramente, la call no lo hace. Simplemente permite que su shell emita lo que sea ... lo que lo convierte en un riesgo de seguridad, como lo describe Python aquí .

En segundo lugar, en el caso de check_output(cmd, shell=True) (solo una canalización) ... cualquier subproceso que se envíe a la _communicate se procesa mediante un subproceso en el método _communicate . ¡Y Popen debe unirse al hilo (esperar en él) antes de esperar, además, a que el subproceso termine!

Además, más trivialmente, procesa stdout como una list que luego debe unirse en una cadena.

En resumen, incluso con argumentos mínimos, check_output pasa mucho más tiempo en los procesos de Python que la call .

He estado usando subprocess.check_output() durante algún tiempo para capturar la salida de los subprocesos, pero me encontré con algunos problemas de rendimiento en ciertas circunstancias. Estoy ejecutando esto en una máquina RHEL6.

El entorno de Python de llamada es compilado por Linux y de 64 bits. El subproceso que estoy ejecutando es un script de shell que finalmente desencadena un proceso python.exe de Windows a través de Wine (por eso, esta locura es otra historia). Como entrada para el script de shell, estoy ingresando una pequeña cantidad de código Python que se pasa a python.exe.

Si bien el sistema tiene una carga moderada / pesada (40 a 70% de utilización de la CPU), he observado que el uso de subprocess.check_output(cmd, shell=True) puede provocar un retraso significativo (hasta ~ 45 segundos) después del subproceso ha finalizado la ejecución antes de que se devuelva el comando check_output. Mirar la salida de ps -efH durante este tiempo muestra el subproceso llamado como sh <defunct> , hasta que finalmente regresa con un estado de salida de cero normal.

Por el contrario, usar subprocess.call(cmd, shell=True) para ejecutar el mismo comando bajo la misma carga moderada / pesada hará que el subproceso regrese inmediatamente sin demora, toda la salida impresa a STDOUT / STDERR (en lugar de devuelta por la función) llamada).

¿Por qué hay un retraso tan significativo solo cuando check_output() está redirigiendo la salida STDOUT / STDERR a su valor de retorno, y no cuando la call() simplemente lo imprime de nuevo a STDOUT / STDERR del padre?


Veamos el código. El .check_output tiene la siguiente espera:

def _internal_poll(self, _deadstate=None, _waitpid=os.waitpid, _WNOHANG=os.WNOHANG, _os_error=os.error, _ECHILD=errno.ECHILD): """Check if child process has terminated. Returns returncode attribute. This method is called by __del__, so it cannot reference anything outside of the local scope (nor can any methods it calls). """ if self.returncode is None: try: pid, sts = _waitpid(self.pid, _WNOHANG) if pid == self.pid: self._handle_exitstatus(sts) except _os_error as e: if _deadstate is not None: self.returncode = _deadstate if e.errno == _ECHILD: # This happens if SIGCLD is set to be ignored or # waiting for child processes has otherwise been # disabled for our process. This child is dead, we # can''t get the status. # http://bugs.python.org/issue15756 self.returncode = 0 return self.returncode

El .call espera usando el siguiente código:

def wait(self): """Wait for child process to terminate. Returns returncode attribute.""" while self.returncode is None: try: pid, sts = _eintr_retry_call(os.waitpid, self.pid, 0) except OSError as e: if e.errno != errno.ECHILD: raise # This happens if SIGCLD is set to be ignored or waiting # for child processes has otherwise been disabled for our # process. This child is dead, we can''t get the status. pid = self.pid sts = 0 # Check the pid and loop as waitpid has been known to return # 0 even without WNOHANG in odd situations. issue14396. if pid == self.pid: self._handle_exitstatus(sts) return self.returncode

Tenga en cuenta que el error relacionado con internal_poll. Se puede ver en http://bugs.python.org/issue15756 . Casi exactamente el problema que está encontrando.

Edición: El otro problema potencial entre .call y .check_output es que .check_output realmente se preocupa por la entrada estándar y la salida estándar e intentará realizar IO contra ambas canalizaciones. Si se está ejecutando en un proceso que se convierte en un estado zombie, es posible que una lectura en contra de una tubería en un estado inactivo esté causando el bloqueo que está experimentando.

En la mayoría de los casos, los estados de zombis se limpian con bastante rapidez, pero no lo harán si, por ejemplo, se interrumpen durante una llamada al sistema (como leer o escribir). Por supuesto, la llamada del sistema de lectura / escritura debería interrumpirse tan pronto como ya no se pueda realizar el IO, pero es posible que esté llegando a algún tipo de condición de carrera en la que las cosas se maten en un mal orden.

La única forma en que puedo pensar para determinar cuál es la causa en este caso es que usted agregue un código de depuración al archivo de subproceso o que invoque al depurador de Python e inicie un retroceso cuando se ejecuta en la condición que está experimentando.