subprocess配合logging stream出现卡死
复现
def run_it(self, cmd):
# 这里的cmd中会产生大量out数据输出到PIPE中
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True,
stderr=subprocess.PIPE, close_fds=True)
log.debug('running:%s' % cmd)
p.wait()
if p.returncode != 0:
log.critical("Non zero exit code:%s executing: %s" % (p.returncode, cmd))
return p.stdout
原因
可能是stdout都输出在subprocess.PIPE中,导致PIPE满了
Popen.wait()
Wait for child process to terminate. Set and return returncode attribute.
Warning This will deadlock when using stdout=PIPE and/or stderr=PIPE and the child process generates enough output to a pipe such that it blocks waiting for the OS pipe buffer to accept more data. Use communicate() to avoid that.
# 警告 当使用 stdout=PIPE 和/或 stderr=PIPE 并且子进程生成足够的输出到管道(PIPE)时,这将死锁,从而阻止等待 OS 管道缓冲区接受更多数据。 使用communication() 来避免这种情况。
Popen.communicate(input=None)
Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate. The optional input argument should be a string to be sent to the child process, or None, if no data should be sent to the child.
# 与进程交互:将数据发送到标准输入。 从 stdout 和 stderr 读取数据,直到到达文件尾。 等待进程终止。 可选的输入参数应该是要发送到子进程的字符串,或者 None,如果没有数据应该发送到子进程
communicate() returns a tuple (stdoutdata, stderrdata).
Note that if you want to send data to the process’s stdin, you need to create the Popen object with stdin=PIPE. Similarly, to get anything other than None in the result tuple, you need to give stdout=PIPE and/or stderr=PIPE too.
# 请注意,如果要将数据发送到进程的 stdin,则需要使用 stdin=PIPE 创建 Popen 对象。 同样,要在结果元组中获得除 None 之外的任何内容,您还需要提供 stdout=PIPE 和/或 stderr=PIPE 。
Note The data read is buffered in memory, so do not use this method if the data size is large or unlimited.
# 注意 读取的数据是缓存在内存中的,所以如果数据量很大或者没有限制,不要使用这种方法。
解决
-
尽量不要将大量数据输出在PIPE中
-
及时将输出数据从PIPE中取走,使用
communicate()
将输出数据从PIPE中读取出来,将数据放在内存中,此时上限便与内存相关;# 使用communicate() def run_it(self, cmd): p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, stderr=subprocess.PIPE, close_fds=True) out, err = p.communicate() p.wait() if p.returncode != 0: log.critical("Non zero exit code:%s executing: %s" % (p.returncode, cmd)) return p.stdout # 及时从管道中取走数据, communicate()内部就是将stdout/stderr读取出来到一个list变量中 def run_it(self, cmd): p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, stderr=subprocess.PIPE, close_fds=True) for line in iter(p.stdout.readline, b''): print line # print to stdout immediately p.stdout.close() p.wait() if p.returncode != 0: log.critical("Non zero exit code:%s executing: %s" % (p.returncode, cmd)) return p.stdout
-
如果stdout超出内存,可以考虑将输出数据写在文件中,
stdout=open('out.out', 'w')
def run_it(self, cmd): p = subprocess.Popen(cmd, shell=True, stdout=open('out.out', 'w'), stderr=subprocess.PIPE, close_fds=True) p.wait() if p.returncode != 0: log.critical("Non zero exit code:%s executing: %s" % (p.returncode, cmd)) return p.stdout
博客内容仅供参考,部分参考他人优秀博文,仅供学习使用