記錄一次因subprocess PIPE 引起的線上故障


sence:python中使用subprocess.Popen(cmd, stdout=sys.STDOUT, stderr=sys.STDERR, shell=True) ,stdout, stderr 為None.

在錯誤中執行是無法捕獲 stderr的內容,后面將上面的改為 subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True),發現是可以拿到 stderr, 但是會遇到大量任務hanging,造成線上事故。

為此特意查詢subprocess的一些參數的說明。

stdin stdout stderr 如果這些參數為 PIPE, 此時會為一個文件句柄,而傳入其他(例如 sys.stdoutNone 等)的則為None

正如這里介紹的一樣,subprocess

而使用 PIPE,卻導致程序 hanging。一般來說不推薦使用 stdout=PIPE stderr=PIPE,這樣會導致一個死鎖,子進程會將輸入的內容輸入到 pipe,直到操作系統從buffer中讀取出輸入的內容。

查詢手冊可以看到確實是這個問題 Refernce

Warning This will deadlock when using stdout=PIPE and/or stderr=PIPE and the child process generates enough output to a pipe such that it blocks waiting for the OS pipe buffer to accept more data. Use communicate() to avoid that.

而在linux中 PIPE 的容量(capacity)是內核中具有固定大小的一塊緩沖區,如果用來接收但不消費就會阻塞,所以當用來接收命令的輸出基本上100% 阻塞所以會導致整個任務 hanging。( -Linux2.6.11 ,pipe capacity 和system page size 一樣(如, i386 為 4096 bytes )。 since Linux 2.6.11+,pipe capacity 為 65536 bytes。)

關於更多的信息可以參考:pipe

所以如果既要拿到對應的輸出進行格式化,又要防止程序hang,可以自己創建一個緩沖區,這樣可以根據需求控制其容量,可以有效的避免hanging。列如:

cmd = "this is complex command"
outPipe = tempfile.SpooledTemporaryFile(bufsize=10*10000)
fileno = outPipe.fileno()
process = subprocess.Popen(cmd,stdout=fileno,stderr=fileno,shell=True)

另外,幾個參數設置的不通的區別如下:

stdout=None 為繼承父進程的句柄,通俗來說為標准輸出。

stderr=STDOUT 重定向錯誤輸出到標准輸出

stdout=PIPE 將標准輸出到linux pipe

Reference

subprocess

subprocess stderr/stdout field is None

subprocess-popen-hanging

pipe size


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM