Python多進程(1)——subprocess與Popen()

  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 ,下面也會詳細介紹剩餘的具名參數。

參數介紹:

  • 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下進程間通訊的手段不少,子進程也徹底可能從建立以後繼續調用

相關文章
相關標籤/搜索