2015-01-06 103 views
9

在子模塊的Python 2.7版的文檔,我發現下面的代碼片段:更換殼牌管道

p1 = Popen(["dmesg"], stdout=PIPE) 
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) 
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits. 
output = p2.communicate()[0] 

來源:https://docs.python.org/2/library/subprocess.html#replacing-shell-pipeline

我不明白這行:p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.

這裏P1 .stdout正在關閉。如果p2退出,它如何讓p1接收SIGPIPE?

+0

[可能來自python subprocess module的例子](http://stackoverflow.com/questions/6046779/explain-example-from-python-subprocess-module) –

回答

4

如果進程嘗試寫入沒有活動進程正在查找的管道,則通常會發送SIGPIPE信號。在外殼管道相當於你的代碼片斷:

`dmesg | grep hda` 

如果之前dmesg完成寫入輸出grep過程中由於某種原因終止,dmesg將收到一個SIGPIPE並終止本身。這將是UNIX/Linux進程的預期行爲(http://en.wikipedia.org/wiki/Unix_signal)。

與此相反,在使用subprocess,如果p2退出之前p1完成生成輸出的Python實現,該SIGPIPE不會發送,因爲實際上仍有看管的過程 - 的Python腳本本身(一個其創建了p1p2)。更重要的是,腳本正在查看管道但不消耗其內容 - 其效果是管道無限期地打開,並且p1卡住了。

顯式關閉p1.stdout從管分離的Python腳本,並使得如不超過p2其他進程看管道 - 這樣,如果p2p1之前結束,p1正確獲得信號,結束自身沒有任何人爲打開管道。

這裏是一個替換地措辭的解釋: http://www.enricozini.org/2009/debian/python-pipes/

+0

「一個沒有活動進程正在查看的管道「而且」仍然是一個看管子的過程「並不是真正精確的措詞。 –

0

docs

The p1.stdout.close() call after starting the p2 is important in order for p1 to receive a SIGPIPE if p2 exits before p1. 

當它試圖寫入一個管沒有連接到一過程中的SIGPIPE信號被髮送到處理另一端。當使用stdin=p1.stdout創建p2時,有兩個進程連接到管道p1.stdout:父級python進程和p2。即使p2過早關閉,父進程仍在運行,因此SIGPIPE信號不會被髮送。 p1.stdout.close()在父進程/調用者進程中關閉p1.stdout,因此將dmesg作爲該文件描述符打開的唯一進程。

換句話說,如果沒有p1.stdout.close()則:

  • p1.stdout仍然在父進程中打開。如果p2退出(即有 沒有人閱讀p1.stdout),則p1將不知道沒有人讀取 p1.stdout,並且將繼續寫入p1.stdout,直到對應的OS管道緩衝區已滿爲止。
  • 萬一P2過早退出,p1.stdout仍然是 在父進程打開,從而SIGPIPE將不會生成。
2

一個希望更系統的解釋:

  • 管道是由操作系統管理的實例。它具有單個讀取端和單個寫入端。
  • 兩端可以通過多個進程打開。儘管如此,仍然只有一個管道。也就是說,多個進程可以共享同一個管道。
  • 已打開其中一個末端的進程擁有相應的文件句柄。該過程可以再次積極close()它!如果進程退出,操作系統會爲您關閉相應的文件句柄。
  • 所有參與過程可以close()代表管道的讀端的文件句柄。沒有錯,這是一個非常好的情況。
  • 現在,如果一個進程將數據寫入到所述管的寫入端和讀取端不再被打開(無進程持有對讀出結束一個打開的文件句柄),符合POSIX標準的操作系統發送SIGPIPE信號在寫作過程中爲它知道有沒有讀者了。

這是標準的機制,通過它接收程序可以隱含告訴它已經停止閱讀的發送程序。你有沒有想過如果

cat bigfile | head -n5 

實際上讀取整個bigfile?不,不會的,因爲cat(從標準輸入讀取5行之後),只要head退出檢索SIGPIPE信號。重要的是欣賞:cat已被設計爲實際上響應SIGPIPE(這是一個重要的工程決策;)):它停止閱讀文件並退出。其他程序設計爲忽略SIGPIPE(故意單獨處理這種情況 - 這在網絡應用程序中很常見)。

如果您在控制過程中保持管道的讀取結束,則禁用所描述的機制。 dmesg將不能注意到grep已退出。

然而,你的榜樣其實不是一個好。 grep hda將讀取整個輸入。 dmesg是首先退出的過程。