python调用外部子进程,通过管道实现异步标准输入和输出的交互
2014-05-26 20:42
836 查看
我们通常会遇到这样的需求:通过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。先看一个简单的例子:
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函数可以把子进程的标准输出改为非阻塞的方式,从而达到我们的目的。这样困扰我许久的问题终于得到了完美解决。代码如下:
另外,调用一些外部程序时,可能需要指定相应的环境变量,方式如下:
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调用外部子进程,通过管道实现异步标准输入和输出的交互
- python调用外部子进程,通过管道实现异步标准输入和输出
- NodeJS用递归实现异步操作的链式调用,完成一个简易的命令行输入输出REPL交互接口
- 通过实现字符串的正向输入逆向输出验证递归调用
- Linux下C编程-----文件操作(1) 通过系统调用简单操作标准输入、标准输出、标准错误
- 通过标准输入输出实现Greenplum文件导入导出
- Python第十天 print >> f,和fd.write()的区别 stdout的buffer 标准输入 标准输出 从控制台重定向到文件 标准错误 重定向 输出流和输入流 捕获sys.exit()调用
- Linux 管道替换标准输入输出 实现2个独立程序代码之间的通信
- QProcess与外部程序的调用(可以通过设置管道来交互)
- 通过异步程序调用(APC)实现的定时功能 CreateWaitableTimer和SetWaitableTimer函数
- 用接口实现通过改外部文件实现判断调用接口内的方法
- jQuery的ajax()检验用户名;通过jQuery的load()/get()/post()方法实现;使用XMLHTTPRequest对象来进行AJAX的异步数据交互
- 通过arp实现输入ip地址输出mac地址
- [python]subprocess调用外部命令屏蔽输出
- C#通过委托和异步调用实现窗体间的通讯
- VC调用CMD命令通过控制台管道获取输出结果
- Python重定向标准输入、标准输出和标准错误
- 【STL】通过流迭代器实现vector的输入输出
- python利用管道popen调用.exe进行交互
- C函数调用-不使用函数返回值,而用参数实现输入/输出的4种模式