热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

linux阻塞总结,PythonSubprocessPopen管道阻塞问题分析解决

使用SubprocessPopen的类库困挠了我一个月的问题终于解决了。一句话就是:等待命令返回不要使用wait(),而是使用communicate()

使用Subprocess Popen的类库困挠了我一个月的问题终于解决了。

一句话就是:等待命令返回不要使用wait(),而是使用communicate(),但注意内存,大输出使用文件。

错误的使用例子

之前的代码这样使用的。

# 不合适的代码

def run_it(self, cmd):

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

这段代码之前用着一直没有问题的,后来不知道为何就不能用了(后面知道了,原来输出内容增加,输出的问题本太长,把管道给堵塞了)。

这样的代码也在之前的一个项目中使用,而且调用的次数有上亿次,也没什么问题。之前倒是也卡住了一次,不过有个大神把问题找到了,因为Python版本低于2.7.6,Python对close_fds的一些实现不太好导致的,没有把管道释放掉,一直卡住。设置close_fds=True。不过这个并没有解决我的问题。

解决了我的问题

当时想着既然卡住了,那我就看看是输出了什么才卡住的,结果现有的代码无法支持我的想法,就换了代码,没想到就不卡住了。

def run_it(cmd):

# _PIPE = subprocess.PIPE

p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True,

stderr=subprocess.PIPE) #, close_fds=True)

log.debug('running:%s' % cmd)

out, err = p.communicate()

log.debg(out)

if p.returncode != 0:

log.critical("Non zero exit code:%s executing: %s" % (p.returncode, cmd))

return p.stdout

看看Python文档信息

Warning

Use communicate() rather than .stdin.write, .stdout.read or .stderr.read to avoid deadlocks due to any of the other OS pipe buffers filling up and blocking the child process.

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.

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.

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.

Note The data read is buffered in memory, so do not use this method if the data size is large or unlimited.

之前没注意,再细看一下文档,感觉豁然开朗。

Linux管道限制,为什么会阻塞呢?

子进程产生一些数据,他们会被buffer起来,当buffer满了,会写到子进程的标准输出和标准错误输出,这些东西通过管道发送给父进程。当管道满了之后,子进程就停止写入,于是就卡住了。

及时取走管道的输出也没有问题

# 及时从管道中取走数据

def run_it(self, cmd):

p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True,

stderr=subprocess.PIPE, close_fds=True)

log.debug('running:%s' % cmd)

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

看了Python的communicate()内部就是将stdout/stderr读取出来到一个list变量中的,最后函数结束时返回。

测试Linux管道阻塞问题

看到别人的例子,一直在想怎么测试输出64K的数据,发现dd这个思路很棒,是见过最优雅的例子了,精确控制输出的长度,其他都是从某些地方搞来大文件导入进来。

#!/usr/bin/env python

# coding: utf-8

# yc@2013/04/28

import subprocess

def test(size):

print 'start'

cmd = 'dd if=/dev/urandom bs=1 count=%d 2>/dev/null' % size

p = subprocess.Popen(args=cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)

#p.communicate()

p.wait() # 这里超出管道限制,将会卡住子进程

print 'end'

# 64KB

test(64 * 1024)

# 64KB + 1B

test(64 * 1024 + 1)

# output :

start

end

start # 然后就阻塞了。

首先测试输出为 64KB 大小的情况。使用 dd 产生了正好 64KB 的标准输出,由 subprocess.Popen 调用,然后使用 wait() 等待 dd 调用结束。可以看到正确的 start 和 end 输出;然后测试比 64KB 多的情况,这种情况下只输出了 start,也就是说程序执行卡在了 p.wait() 上,程序死锁。

总结

那死锁问题如何避免呢?官方文档里推荐使用 Popen.communicate()。这个方法会把输出放在内存,而不是管道里,所以这时候上限就和内存大小有关了,一般不会有问题。而且如果要获得程序返回值,可以在调用 Popen.communicate() 之后取 Popen.returncode 的值。

但真的如果超过内存了,那么要考虑比如文件 stdout=open("process.out", "w") 的方式来解决了,不能使用管道了。

另外说一下。管道的要用清楚,不要随意的乱世用管道。比如没有input的时候,那么stdin就不要用管道了。

还有不要把简单的事情复杂化。比如echo 1 > /sys/linux/xxx修改文件,这么简单的功能就不要用Linux的shell调用了,使用Python自带的 open('file', 'w').write('1') 。尽量保持Python范。

参考



推荐阅读
  • 基于dlib的人脸68特征点提取(眨眼张嘴检测)python版本
    文章目录引言开发环境和库流程设计张嘴和闭眼的检测引言(1)利用Dlib官方训练好的模型“shape_predictor_68_face_landmarks.dat”进行68个点标定 ... [详细]
  • Python操作MySQL(pymysql模块)详解及示例代码
    本文介绍了使用Python操作MySQL数据库的方法,详细讲解了pymysql模块的安装和连接MySQL数据库的步骤,并提供了示例代码。内容涵盖了创建表、插入数据、查询数据等操作,帮助读者快速掌握Python操作MySQL的技巧。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 开源Keras Faster RCNN模型介绍及代码结构解析
    本文介绍了开源Keras Faster RCNN模型的环境需求和代码结构,包括FasterRCNN源码解析、RPN与classifier定义、data_generators.py文件的功能以及损失计算。同时提供了该模型的开源地址和安装所需的库。 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • 本文讨论了如何使用GStreamer来删除H264格式视频文件中的中间部分,而不需要进行重编码。作者提出了使用gst_element_seek(...)函数来实现这个目标的思路,并提到遇到了一个解决不了的BUG。文章还列举了8个解决方案,希望能够得到更好的思路。 ... [详细]
  • Python已成为全球最受欢迎的编程语言之一,然而Python程序的安全运行存在一定的风险。本文介绍了Python程序安全运行需要满足的三个条件,即系统路径上的每个条目都处于安全的位置、"主脚本"所在的目录始终位于系统路径中、若python命令使用-c和-m选项,调用程序的目录也必须是安全的。同时,文章还提出了一些预防措施,如避免将下载文件夹作为当前工作目录、使用pip所在路径而不是直接使用python命令等。对于初学Python的读者来说,这些内容将有所帮助。 ... [详细]
  • 通过Anaconda安装tensorflow,并安装运行spyder编译器的完整教程
    本文提供了一个完整的教程,介绍了如何通过Anaconda安装tensorflow,并安装运行spyder编译器。文章详细介绍了安装Anaconda、创建tensorflow环境、安装GPU版本tensorflow、安装和运行Spyder编译器以及安装OpenCV等步骤。该教程适用于Windows 8操作系统,并提供了相关的网址供参考。通过本教程,读者可以轻松地安装和配置tensorflow环境,以及运行spyder编译器进行开发。 ... [详细]
  • 本文介绍了协程的概念和意义,以及使用greenlet、yield、asyncio、async/await等技术实现协程编程的方法。同时还介绍了事件循环的作用和使用方法,以及如何使用await关键字和Task对象来实现异步编程。最后还提供了一些快速上手的示例代码。 ... [详细]
  • Python中的PyInputPlus模块原文:https ... [详细]
  • Django + Ansible 主机管理(有源码)
    本文给大家介绍如何利用DjangoAnsible进行Web项目管理。Django介绍一个可以使Web开发工作愉快并且高效的Web开发框架,能够以最小的代价构建和维护高 ... [详细]
  • 很多时候在注册一些比较重要的帐号,或者使用一些比较重要的接口的时候,需要使用到随机字符串,为了方便,我们设计这个脚本需要注意 ... [详细]
  • 一文了解Python collections模块中的deque用法[python头条资讯]
    Python中文网有大量免费的Python入门教程,欢迎大家来学习。collections是Python内建的一个集合模块,deque是双边队列,具有队列和栈的性质,在list的基 ... [详细]
author-avatar
桑语敬
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有