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

來源:互聯網
上載者:User

標籤:

  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.returncode0

  這裡也可以使用 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 ms64 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 1001msrtt min/avg/max/mdev = 37.335/37.410/37.486/0.207 ms>>> print p0>>> p = subprocess.check_call([‘ping‘ ,‘-c‘, ‘4‘, ‘[email protected]$#@!(*^.com‘])ping: unknown host [email protected]$#@!(*^.comTraceback (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‘, ‘[email protected]$#@!(*^.com‘]‘ returned non-zero exit status 2>>> print p0

  

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 subprocesschild1 = 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下處理序間通訊的手段很多,子進程也完全可能從建立之後繼續調用

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.