Python多进程方面涉及的模块主要包括:html
本文主要介绍 subprocess 模块及其提供的 Popen 类,以及如何使用该构造器在一个进程中建立新的子进程。此外,还会简要介绍 subprocess 模块提供的其余方法与属性,这些功能上虽然没有 Popen 强大的工具,在某些状况下却很是方便和高效。python
本文的目录以下:shell
1. subprocess.Popen 类编程
2. Popen 对象的属性缓存
3. Popen 对象的方法数据结构
4. subprocess模块的其余简便方法python2.7
5. subprocess模块的其余属性函数
6. subprocess模块定义的异常工具
subprocess.Popen 类spa
经过调用:
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 ,下面也会详细介绍剩余的具名参数。
参数介绍:
对于 stdin, stdout 和 stderr 而言,若是他们是 None(默认状况),那么子进程使用和父进程相同的标准流文件。
父进程若是想要和子进程经过 communicate() 方法通讯,对应的参数必须是 subprocess.PIPE(见下文例4);
固然 stdin, stdout 和 stderr 也能够是已经打开的 file 对象,前提是以合理的方式打开,好比 stdin 对应的文件必需要可读等。
同 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 ... >>> p.returncode >>> p.wait() 0 >>> p.returncode 0
这里也可使用 p = subprocess.Popen(['ls', '-cl']) 来建立子进程。
Popen 对象的属性
Popen建立的子进程有一些有用的属性,假设 p 是 Popen 建立的子进程,p 的属性包括:
1.
p.pid
子进程的PID。
2.
p.returncode
该属性表示子进程的返回状态,returncode可能有多重状况:
3.
p.stdin, p.stdout, p.stderr
子进程对应的一些初始文件,若是调用Popen()的时候对应的参数是subprocess.PIPE,则这里对应的属性是一个包裹了这个管道的 file 对象,
Popen 对象的方法
1.
p.poll()
检查子进程 p 是否已经终止,返回 p.returncode 属性 (参考下文 Popen 对象的属性);
2.
p.wait()
等待子进程 p 终止,返回 p.returncode 属性;
注意:
wait() 当即阻塞父进程,直到子进程结束!
3.
p.communicate(input=None)
和子进程 p 交流,将参数 input (字符串)中的数据发送到子进程的 stdin,同时从子进程的 stdout 和 stderr 读取数据,直到EOF。
返回值:
二元组 (stdoutdata, stderrdata) 分别表示从标准出和标准错误中读出的数据。
父进程调用 p.communicate() 和子进程通讯有如下限制:
(1) 只能经过管道和子进程通讯,也就是说,只有调用 Popen() 建立子进程的时候参数 stdin=subprocess.PIPE,才能经过 p.communicate(input) 向子进程的 stdin 发送数据;只有参数 stout 和 stderr 也都为 subprocess.PIPE ,才能经过p.communicate() 从子进程接收数据,不然接收到的二元组中,对应的位置是None。
(2)父进程从子进程读到的数据缓存在内存中,所以commucate()不适合与子进程交换过大的数据。
注意:
communicate() 当即阻塞父进程,直到子进程结束!
4.
p.send_signal(signal)
向子进程发送信号 signal;
5.
p.terminate()
终止子进程 p ,等于向子进程发送 SIGTERM 信号;
6.
p.kill()
杀死子进程 p ,等于向子进程发送 SIGKILL 信号;
subprocess模块的其余方法
1.
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
父进程直接建立子进程执行程序,而后等待子进程完成
返回值:
call() 返回子进程的 退出状态 即 child.returncode 属性;
2.
subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
父进程直接建立子进程执行程序,而后等待子进程完成,具体可使用的参数,参考上文 Popen 类的介绍。
返回值:
不管子进程是否成功,该函数都返回 0;可是
若是子进程的退出状态不是0,check_call() 抛出异常 CalledProcessError,异常对象中包含了 child.returncode 对应的返回码。
例2:
check_call()正常与错误执行命令
>>> p = subprocess.check_call(['ping' ,'-c', '2', 'www.baidu.com']) PING www.a.shifen.com (220.181.111.188) 56(84) bytes of data. 64 bytes from 220.181.111.188: icmp_seq=1 ttl=42 time=37.4 ms 64 bytes from 220.181.111.188: icmp_seq=2 ttl=42 time=37.3 ms --- www.a.shifen.com ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 37.335/37.410/37.486/0.207 ms >>> print p 0 >>> p = subprocess.check_call(['ping' ,'-c', '4', 'www.!@$#@!(*^.com']) ping: unknown host www.!@$#@!(*^.com Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python2.7/subprocess.py", line 540, in check_call raise CalledProcessError(retcode, cmd) subprocess.CalledProcessError: Command '['ping', '-c', '4', 'www.!@$#@!(*^.com']' returned non-zero exit status 2 >>> print p 0
3.
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)
父进程直接建立子进程执行程序,以字符串的形式返回子进程的输出。
返回值:
字符串形式的子进程的输出结果,可是,
若是子进程的 退出状态 不是0,那么抛出异常 CalledProcessError,异常对象中包含了 child.returncode 对应的返回码。
注意:
check_output() 的函数签名中没有参数 stdout,调用该方法时,子进程的输出默认就返回给父进程。
例3:
check_output() 调用的子进程正常与错误退出
>>> subprocess.check_output(["echo", "Hello World!"]) 'Hello World!\n' >>> subprocess.check_output("exit 1", shell=True) Traceback (most recent call last): ... subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
注意:
使用上面提到的三个方法:call()、check_call() 和 check_output() 时,尽可能不要将参数 stderr 和 stdout 设置为 subprocess.PIPE,这几个函数默认都会等待子进程完成,子进程产生大量的输出数据若是形成管道堵塞,父进程再等待子进程完成可能形成死锁。
subprocess模块的其余属性
subprocess.PIPE
调用本模块提供的若干函数时,做为 std* 参数的值,为标准流文件打开一个管道。
例4:
使用管道链接标准流文件
import subprocess child1 = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE) child2 = subprocess.Popen(['wc', '-l'], stdin=child1.stdout, stdout=subprocess.PIPE) out = child2.communicate() child1.wait() child2.wait() print(out)
这里将子进程 child1 的标准输出做为子进程 child2 的标准输入,父进程经过 communicate() 读取 child2 的标准输出后打印。
subprocess.STDOUT
调用本模块提供的若干函数时,做为 stderr 参数的值,将子进程的标准错误输出打印到标准输出。
subprocess模块定义的异常
exception subprocess.CalledProcessError
(1)何时可能抛出该异常:调用 check_call() 或 check_output() ,子进程的退出状态不为 0 时。
(2)该异常包含如下信息:
总结
本文介绍了Python subprocess的基本用法,使用 Popen 能够在Python进程中建立子进程,若是只对子进程的执行退出状态感兴趣,能够调用 subprocess.call() 函数,若是想经过异常处理机制解决子进程异常退出的情形,能够考虑使用 subprocess.check_call() 和 subprocess.check_output。若是但愿得到子进程的输出,能够调用 subprocess.check_output(),但 Popen() 无疑是功能最强大的。
subprocess模块的缺陷在于默认提供的父子进程间通讯手段有限,只有管道;同时建立的子进程专门用来执行外部的程序或命令。
Linux下进程间通讯的手段不少,子进程也彻底可能从建立以后继续调用