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
博客内容仅供参考,部分参考他人优秀博文,仅供学习使用
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix