>  기사  >  백엔드 개발  >  Python은 외부 하위 프로세스를 호출하여 파이프를 통해 비동기 표준 입력 및 출력을 구현합니다.

Python은 외부 하위 프로세스를 호출하여 파이프를 통해 비동기 표준 입력 및 출력을 구현합니다.

大家讲道理
大家讲道理원래의
2016-11-07 17:06:381024검색

우리는 일반적으로 C++ 또는 기타 하위 수준 언어를 통해 복잡한 기능 모듈을 구현하고, 데이터를 쿼리하기 위해 웹 기반 데모를 구축해야 하는 등의 요구 사항에 직면합니다. Python 언어의 강력함과 단순성으로 인해 데모 구축에 매우 적합합니다. Flask 프레임워크와 jinja2 모듈 기능은 Python에 편리한 웹 개발 기능을 제공합니다. 동시에 Python은 다른 언어의 코드와 쉽게 상호 작용할 수 있습니다. 따라서 우리는 Demo 개발 도구로 Python을 선택했습니다. 우리가 호출해야 하는 모듈(기본 서비스 제공)이 표준 입력을 통해 루프에서 데이터를 읽고 처리 후 표시된 출력에 결과를 쓴다고 가정합니다. 이 시나리오는 Linux 환경에서 매우 일반적이며 Linux의 강력한 리디렉션 기능에 의존합니다. . 그러나 불행하게도 기본 모듈에는 초기화 프로세스가 너무 많기 때문에 모든 쿼리 요청에 대해 기본 모듈을 호출하는 하위 프로세스를 다시 생성할 수 없습니다. 해결책은 자식 프로세스를 한 번만 생성한 다음 각 요청에 대해 파이프를 통해 자식 프로세스와 상호 작용하는 것입니다.

Python의 하위 프로세스 모듈은 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)

통신 함수는 하위 프로세스의 표준 출력과 오류를 표시하는 출력 데이터가 포함된 튜플(stdoutdata, stderrdata)을 반환합니다. . 그러나 Popen 객체의 통신 함수는 상위 프로세스를 차단하고 파이프도 닫으므로 각 Popen 객체는 통신 함수를 한 번만 호출할 수 있습니다. 요청이 여러 개인 경우 Popen 객체를 다시 생성해야 합니다(자식 프로세스 다시 초기화). 우리의 요구를 충족시킬 수 없습니다.

따라서 Popen 객체의 stdin 및 stdout 객체에 데이터를 쓰고 읽어야만 우리의 요구 사항을 충족할 수 있습니다. 그러나 불행히도 하위 프로세스 모듈은 기본적으로 하위 프로세스가 종료될 때 한 번만 표준 출력을 실행하고 읽습니다. 하위 프로세스와 os.popen*은 모두 한 번만 입력과 출력을 허용하고 프로세스가 종료될 때만 출력을 읽을 수 있습니다.

몇몇 조사 끝에 하위 프로세스가 fcntl 함수를 통해 처리될 수 있다는 것을 발견했습니다. fcntl 모듈 우리의 목적을 달성하기 위해 프로세스의 표준 출력이 비차단 모드로 변경됩니다. 오랫동안 나를 괴롭혔던 이 문제가 드디어 완벽하게 해결되었습니다. 코드는 다음과 같습니다.

#!/usr/bin/python                                                                                                                                                      
# -*- coding: utf-8 -*-
# author: weisu.yxd@taobao.com
from subprocess import Popen, PIPE
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, e=1, tr=5, stderr=0):
    time.sleep(t)
    if tr < 1:
        tr = 1 
    x = time.time()+t
    r = &#39;&#39;
    pr = self.process.stdout
    if stderr:
      pr = self.process.stdout
    while time.time() < x or r:
        r = pr.read()
        if r is None:
            if e:
                raise Exception(message)
            else:
                break
        elif r:
            return r.rstrip()
        else:
            time.sleep(max((x-time.time())/tr, 0))
    return r.rstrip()
if __name__ == "__main__":
  ServerArgs = [&#39;/home/weisu.yxd/QP/trunk/bin/normalizer&#39;, &#39;/home/weisu.yxd/QP/trunk/conf/stopfile.txt&#39;]
  server = Server(ServerArgs)
  test_data = &#39;在云端&#39;, &#39;云梯&#39;, &#39;摩萨德&#39;, &#39;Alisa&#39;, &#39;iDB&#39;, &#39;阿里大数据&#39;
  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)

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.