Python多进程

Python多进程方面涉及到的模块主要包括:

  • subprocess: 可以在当前程序中执行其他程序或命令
  • mmap:提供一种基于内存的进程间通信机制
  • multiprocessing:提供支持多处理器技术的多进程编程接口,并且接口的设计最大程度的保持了和threading模块的一致,并与理解和使用

本文主要介绍 subprocess 模块及其提供的 Popen 类,以及如何使用该构造器在一个进程中创建新的子进程。此外,还会简要介绍 subprocess 模块提供的其他方法与属性,这些功能上虽然没有 Popen 强大的工具,在某些情况下却非常方便和高效。

本文的目录如下:

  1. subprocess.Popen类
  2. Popen对象的属性
  3. Popen对象的方法
  4. subprocess模块的其他简便方法
  5. subprocess模块的其他属性
  6. subprocess模块定义的异常

subprocess.Popen 类

subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)

创建并返回一个子进程,并在这个子进程中执行指定的程序

实例化 Popen 可以通过许多参数详细定制子进程的环境,但是只有一个参数是必须的,即位置参数 args ,下面也会详细介绍剩余的具名参数。

参数介绍:

  • 要执行的命令或可执行文件的路径。一个由字符串组成的序列(通常是列表),列表的第一个元素是可执行程序的路径,剩下的是传给这个程序的参数,如果没有要传给这个程序的参数,args 参数可以仅仅是一个字符串。
  • bufsize:控制 stdin, stdout, stderr 等参数指定的文件的缓冲,和打开文件的 open()函数中的参数 bufsize 含义相同。
  • executable:如果这个参数不是 None,将替代参数 args 作为可执行程序;
  • stdin:指定子进程的标准输入;
  • stdout:指定子进程的标准输出;
  • stderr:指定子进程的标准错误输出;

对于 stdin, stdout 和 stderr 而言,如果他们是 None(默认情况),那么子进程使用和父进程相同的标准流文件。父进程如果想要和子进程通过 communicate() 方法通信,对应的参数必须是 subprocess.PIPE(见下文例4);当然 stdin, stdout 和 stderr 也可以是已经打开的 file 对象,前提是以合理的方式打开,比如 stdin 对应的文件必须要可读等。

  • preexec_fn:默认是None,否则必须是一个函数或者可调用对象,在子进程中首先执行这个函数,然后再去执行子进程指定的程序或Shell。
  • close_fds:布尔型变量,为 True 时,在子进程执行前强制关闭所有除 stdin,stdout和stderr外的文件;
  • shell:布尔型变量,明确要求使用shell运行程序,与参数 executable 一同指定子进程运行在什么 Shell 中——如果executable=None 而 shell=True,则使用 /bin/sh 来执行 args 指定的程序;也就是说,Python首先起一个shell,再用这个shell来解释指定运行的命令。
  • cwd:代表路径的字符串,指定子进程运行的工作目录,要求这个目录必须存在;
  • env:字典,键和值都是为子进程定义环境变量的字符串;
  • universal_newline:布尔型变量,为 True 时,stdout 和 stderr 以通用换行(universal newline)模式打开
  • startupinfo:见下一个参数;
  • creationfalgs:最后这两个参数是Windows中才有的参数,传递给Win32的CreateProcess API调用。

同 Linux 中创建子进程类似,父进程创建完子进程后,并不会自动等待子进程执行,父进程在子进程之前退出将导致子进程成为孤儿进程,孤儿进程统一由 init 进程接管,负责其终止后的回收工作。

如果父进程在子进程之后终止,但子进程终止时父进程没有进行最后的回收工作,子进程残留的数据结构称为僵尸进程。大量僵尸进程将耗费系统资源,因此父进程及时等待和回收子进程是必要的,除非能够确认自己比子进程先终止,从而将回收工作过渡给 init 进程。

这个等待和回收子进程的操作就是wait()函数,下文中将会介绍。

例1:创建一个子进程,然后执行一个简单的命令

>>> import subprocess
>>> p = subprocess.Popen('ls -l', shell=True)
>>> total 164
-rw-r--r--  1 root root   133 Jul  4 16:25 admin-openrc.sh
-rw-r--r--  1 root root   268 Jul 10 15:55 admin-openrc-v3.sh
...
# 等待子进程结束,正常退出返回0
>>> p.wait()
0
>>> p.returncode
0

这里也可以使用 p = subprocess.Popen([‘ls’, ‘-cl’]) 来创建子进程。

Popen对象属性

1.返回子进程的pid

p.pid

2.返回子进程的返回状态,returncode可能有多重情况:

  • None —子进程尚未结束
  • 0 — 子进程正常结束
  • >0 — 子进程异常退出,returncode对应于出错码
  • <0 — 子进程被信号杀掉
p.returncode

3.p.stdin, p.stdout, p.stderr,必须是一个可读的文件对象,如果要与父进程通信,必须设置为subprocess.PIPE

Popen对象的方法

1.检查子进程p是否已经终止,返回p.returncode属性

p.poll()

2.等待子进程p终止,返回p.returncode属性

p.wait()

3.和子进程 p 交流,将参数 input (字符串)中的数据发送到子进程的 stdin,同时从子进程的 stdout 和 stderr 读取数据,直到EOF。

p.communicate(input=None)

返回值:

二元组 (stdoutdata, stderrdata) 分别表示从标准出和标准错误中读出的数据。

父进程调用 p.communicate() 和子进程通信有以下限制:

  1. 只能通过管道和子进程通信,也就是说,只有调用 Popen() 创建子进程的时候参数 stdin=subprocess.PIPE,才能通过 p.communicate(input) 向子进程的 stdin 发送数据;只有参数 stout 和 stderr 也都为 subprocess.PIPE ,才能通过p.communicate() 从子进程接收数据,否则接收到的二元组中,对应的位置是None。
  2. 父进程从子进程读到的数据缓存在内存中,因此commucate()不适合与子进程交换过大的数据。

4.向子进程发送信号 signal

p.send_signal(signal)

5.终止子进程 p ,等于向子进程发送 SIGTERM 信号

p.terminate()

6.杀死子进程 p ,等于向子进程发送 SIGKILL 信号

p.kill()

subprocess模块的其他方法

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)

父进程直接创建子进程执行程序,然后等待子进程完成

返回值:

  • 0 — 子进程正常结束

  • >0 — 子进程异常退出

subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False)

父进程直接创建子进程执行程序,然后等待子进程完成,具体可以使用的参数,参考上文 Popen 类的介绍。

返回值:

  • 0 — 子进程正常结束

  • >0 — 子进程异常退出,check_call() 抛出异常 CalledProcessError,异常对象中包含了 child.returncode 对应的返回码。

subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)

父进程直接创建子进程执行程序,以字符串的形式返回子进程的输出

返回值:

  • 字符串形式的子进程的输出结果,如果子进程的 退出状态 不是0,那么抛出异常 CalledProcessError,异常对象中包含了 child.returncode 对应的返回码。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值