我们通常会遇到这样的需求:通过C++或其他较底层的语言实现了一个复杂的功能模块,需要搭建一个基于Web的Demo,方法查询数据。由于Python语言的强大和简洁,其用来搭建Demo非常合适,Flask框架和jinja2模块功能为python提供了方便的web开发能力。同时,python能够很方便的同其他语言的代码交互。因此我们选择python作为开发Demo的工具。假设我们需要调用的模块(提供底层服务)通过标准输入循环读入数据,处理完毕后把结果写出到标出输出,这样的场景在Linux环境下很常见,依赖于Linux强大的重定向能力。然而,非常不幸的是,底层模块有一个很重的初始化过程,因此我们不能够每次查询请求都去重新生成调用底层模块的子进程。解决方案就是只生成一次子进程,然后对每个请求通过管道(pipe)来和子进程交互。

Python的subprocess模块可以很容易地生成子进程,类似Linux系统调用fork和exec。subprocess模块的Popen对象可能以非阻塞的方式调用外部可执行程序,因此我们使用Poen对象来实现需求。如果我们想要把数据写入子进程的标准输入stdin,那么在创建Popen对象的时候就需要指定参数stdin为subprocess.PIPE;同样,如果我们需要从子进程的标准输出中读取数据,那么在创建Popen对象的时候就需要指定参数stdout为subprocess.PIPE。先看一个简单的例子:

from subprocess import Popen, PIPE

p = Popen('less', stdin=PIPE, stdout=PIPE)
p.communicate('Line number %d.\n' % x)

communicate函数返回一个二元组(stdoutdata, stderrdata),包含了子进程的标准输出和标出错误的输出数据。然而,由于Popen对象的communicate函数会阻塞父进程,同时还会关闭管道,因此每个Popen对象只能调用一次communicate函数,如果有多个请求必须重新生成Popen对象(重新初始化子进程),不能满足我们的需求。

因此,我们只有往Popen对象的stdin和stdout对象里写入和读取数据才能实现我们的需求。然而,不幸的是subprocess模块默认情况下只运行在子进程结束的时候读取一次标准输出。Both subprocess and os.popen* only allow input and output one time, and the output to be read only when the process terminates

进过一番研究之后我发现通过fcntl模块的fcntl函数可以把子进程的标准输出改为非阻塞的方式,从而达到我们的目的。这样困扰我许久的问题终于得到了完美解决。代码如下:

 #!/usr/bin/python
# -*- coding: utf-8 -*-
# author:
from subprocess import Popen, PIPE
import select
import fcntl, os
import time class Server(object):
def __init__(self, args, server_env = None):
if server_env:
self.process = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=server_env)
else:
self.process = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
flags = fcntl.fcntl(self.process.stdout, fcntl.F_GETFL)
fcntl.fcntl(self.process.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) def send(self, data, tail = '\n'):
self.process.stdin.write(data + tail)
self.process.stdin.flush() def recv(self, t=.1, stderr=0):
r = ''
pr = self.process.stdout
if stderr:
pr = self.process.stdout
while True:
if not select.select([pr], [], [], 0)[0]:
time.sleep(t)
continue
r = pr.read()
return r.rstrip()
return r.rstrip() if __name__ == "__main__":
ServerArgs = ['/path/to/server', '/path/to/args']
server = Server(ServerArgs)
test_data = '拿铁', '咖啡'
for x in test_data:
server.send(x)
print x, server.recv()

另外,调用一些外部程序时,可能需要指定相应的环境变量,方式如下:

  my_env = os.environ
my_env["LD_LIBRARY_PATH"] = "/path/to/lib"
server = server.Server(cmd, my_env)

python调用外部子进程,通过管道实现异步标准输入和输出的交互的更多相关文章

  1. Python调用外部系统命令

    利用Python调用外部系统命令的方法可以提高编码效率.调用外部系统命令完成后可以通过获取命令执行返回结果码.执行的输出结果进行进一步的处理.本文主要描述Python常见的调用外部系统命令的方法,包括 ...

  2. Python 调用外部命令

    python 可以使用 os 模块来调用外部的 Linux Shell 命令,常用的方法如下: os.system():结果输出在终端上,捕获不到os.popen() : 结果返回一个对象,即标准输出 ...

  3. Python的subprocess子进程和管道进行交互

    在很久以前,我写了一个系列,Python和C和C++的交互,如下 http://blog.csdn.net/marising/archive/2008/08/28/2845339.aspx 目的是解决 ...

  4. python模拟鼠标键盘操作 GhostMouse tinytask 调用外部脚本或程序 autopy右键另存为

    0.关键实现:程序窗口前置 python 通过js控制滚动条拉取全文 通过psutil获取pid窗口句柄,通过win32gui使程序窗口前置 通过pyauto实现右键菜单和另存为操作 1.参考 aut ...

  5. 进程&线程(三):外部子进程subprocess、异步IO、协程、分布式进程

    1.外部子进程subprocess python之subprocess模块详解--小白博客 - 夜风2019 - 博客园 python subprocess模块 - lincappu - 博客园 之前 ...

  6. 如何在Python脚本中调用外部命令(就像在linux shell或Windows命令提示符下输入一样)

    如何在Python脚本中调用外部命令(就像在linux shell或Windows命令提示符下输入一样) python标准库中的subprocess可以解决这个问题. from subprocess ...

  7. Zabbix调用外部脚本发送邮件:python编写脚本

    Zabbix调用外部脚本发送邮件的时候,会在命令行传入两个参数,第一个参数就是要发送给哪个邮箱地址,第二个参数就是邮件信息,为了保证可以传入多个参数,所以假设有多个参数传入 #!/usr/bin/en ...

  8. Python调用C可执行程序(subprocess) 分类: python 服务器搭建 C/C++ shell 2015-04-13 21:03 87人阅读 评论(0) 收藏

    从Python 2.4开始,Python引入subprocess模块来管理子进程,以取代一些旧模块的方法:如 os.system.os.spawn.os.popen.popen2.commands. ...

  9. python 调用shell命令三种方法

    #!/usr/bin/python是告诉操作系统执行这个脚本的时候,调用/usr/bin下的python解释器: #!/usr/bin/env python这种用法是为了防止操作系统用户没有将pyth ...

随机推荐

  1. 自定义Toast解决快速点击时重复弹出,排队无止尽

    解决办法:自定义MyToast类: public class MyToast { /** 之前显示的内容 */ private static String oldMsg ; /** Toast对象 * ...

  2. js 正则表达式 转至(七郎's Blog)

    //匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线 var re =new RegExp("^[a-zA-Z][a-zA-Z0-9_]{5,19}$"); if( ...

  3. Android 终于官方支持按百分比来设置控件的宽高了

    dependencies { compile 'com.android.support:percent:22.2.0' } 支持布局 PercentRelativeLayout <android ...

  4. 深入理解javascript中实现面向对象编程方法

    介绍Javascript中面向对象编程思想之前,需要对以下几个概念有了解: 1. 浅拷贝和深拷贝:程序在运行过程中使用的变量有在栈上的变量和在堆上的变量,在对象或者变量的赋值操作过程中,大多数情况先是 ...

  5. VLOOKUP

    vlookup(查找目标,查找范围, 返回值的列数,精确或模糊查找) 1. 查找目标:查找的内容或者单元格引用. 2. 查找范围: 选定一个查找区域. 注意一: 查找目标一定要在该区域的第一列. 注意 ...

  6. 基于Maven site的穷人的本地知识管理系统

    1 Motivation On daily study or development, a simple knowledge management system is required. In the ...

  7. js判断类型方法

    在JavaScript中,有5种基本数据类型和1种复杂数据类型,基本数据类型有:Undefined, Null,Boolean, Number和String:复杂数据类型是Object,Object中 ...

  8. 对C语言的知识与能力予以自评

    看到一个问卷不错,拟作为第三次作业的部分内容. 你对自己的未来有什么规划?做了哪些准备?我准备在将来成为一名合格的软件工作人员,我已经在平时有空的时间里着手代码的练习. 你认为什么是学习?学习有什么用 ...

  9. Swing图形用户界面

    package test; import java.awt.event.ActionEvent;import java.awt.event.ActionListener; import javax.s ...

  10. iOS开发之直接使用UISearchBar

    iOS开发中经常需要使用SearchBar,我们可以选择使用UISearchBar+UISearchController或者UISearchBar+UISearchDisplayController( ...