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