设为首页 收藏本站
查看: 951|回复: 0

[经验分享] Python多进程(1)——subprocess与Popen()

[复制链接]

尚未签到

发表于 2015-11-30 14:55:55 | 显示全部楼层 |阅读模式
  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:要执行的命令或可执行文件的路径。一个由字符串组成的序列(通常是列表),列表的第一个元素是可执行程序的路径,剩下的是传给这个程序的参数,如果没有要传给这个程序的参数,args 参数可以仅仅是一个字符串。
  • bufsize:控制 stdin, stdout, stderr 等参数指定的文件的缓冲,和打开文件的 open()函数中的参数 bufsize 含义相同。
  • executable:如果这个参数不是 None,将替代参数 args 作为可执行程序;
  • stdin:指定子进程的标准输入;
  • stdout:指定子进程的标准输出;
  • stderr:指定子进程的标准错误输出;
  对于 stdin, stdoutstderr 而言,如果他们是 None(默认情况),那么子进程使用和父进程相同的标准流文件。
  父进程如果想要和子进程通过 communicate() 方法通信,对应的参数必须是 subprocess.PIPE(见下文例4);
  当然 stdin, stdoutstderr 也可以是已经打开的 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
...
>>> 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可能有多重情况:


  • None —— 子进程尚未结束;
  • ==0 —— 子进程正常退出;
  • > 0—— 子进程异常退出,returncode对应于出错码;
  • < 0—— 子进程被信号杀掉了。
  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)该异常包含以下信息:


  • returncode:子进程的退出状态;
  • cmd:创建子进程时指定的命令;
  • output:如果是调用 check_output() 时抛出的该异常,这里包含子进程的输出,否则该属性为None。
  
  总结
  本文介绍了Python subprocess的基本用法,使用 Popen 可以在Python进程中创建子进程,如果只对子进程的执行退出状态感兴趣,可以调用 subprocess.call() 函数,如果想通过异常处理机制解决子进程异常退出的情形,可以考虑使用 subprocess.check_call() 和 subprocess.check_output。如果希望获得子进程的输出,可以调用 subprocess.check_output(),但 Popen() 无疑是功能最强大的。
  subprocess模块的缺陷在于默认提供的父子进程间通信手段有限,只有管道;同时创建的子进程专门用来执行外部的程序或命令。
  Linux下进程间通信的手段很多,子进程也完全可能从创建之后继续调用

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-145485-1-1.html 上篇帖子: 密歇根大学 下篇帖子: PEP8 Python 编码规范
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表