1. 程式人生 > 實用技巧 >程序通訊----管道(pipe)

程序通訊----管道(pipe)

Linux管道的實現機制

在Linux中,管道是一種使用非常頻繁的通訊機制。從本質上說,管道也是一種檔案,但它又和一般的檔案有所不同,管道可以克服使用檔案進行通訊的兩個問題,具體表現為:

·限制管道的大小。實際上,管道是一個固定大小的緩衝區。在Linux中,該緩衝區的大小為1頁,即4K位元組,使得它的大小不象檔案那樣不加檢驗地增長。使用單個固定緩衝區也會帶來問題,比如在寫管道時可能變滿,當這種情況發生時,隨後對管道的write()呼叫將預設地被阻塞,等待某些資料被讀取,以便騰出足夠的空間供write()呼叫寫。

·讀取程序也可能工作得比寫程序快。當所有當前程序資料已被讀取時,管道變空。當這種情況發生時,一個隨後的read()呼叫將預設地被阻塞,等待某些資料被寫入,這解決了read()呼叫返回檔案結束的問題。

注意:從管道讀資料是一次性操作,資料一旦被讀,它就從管道中被拋棄,釋放空間以便寫更多的資料。

1.管道的結構

在Linux中,管道的實現並沒有使用專門的資料結構,而是藉助了檔案系統的file結構和VFS的索引節點inode。通過將兩個file結構指向同一個臨時的VFS索引節點,而這個VFS索引節點又指向一個物理頁面而實現的。

wKiom1eMxlqCws7kAABk688oTZ8777.png

兩個file資料結構,但它們定義檔案操作例程地址是不同的,其中一個是向管道中寫入資料的例程地址,而另一個是從管道中讀出資料的例程地址。這樣,使用者程式的系統呼叫仍然是通常的檔案操作,而核心卻利用這種抽象機制實現了管道這一特殊操作。

2.管道的讀寫

管道實現的原始碼在fs/pipe.c中,在pipe.c中有很多函式,其中有兩個函式比較重要,即管道讀函式pipe_read()和管道寫函式pipe_wrtie()。管道寫函式通過將位元組複製到VFS索引節點指向的實體記憶體而寫入資料,而管道讀函式則通過複製實體記憶體中的位元組而讀出資料。當然,核心必須利用一定的機制同步對管道的訪問,為此,核心使用了鎖、等待佇列和訊號。

當寫程序向管道中寫入時,它利用標準的庫函式write(),系統根據庫函式傳遞的檔案描述符,可找到該檔案的file結構。file結構中指定了用來進行寫操作的函式(即寫入函式)地址,於是,核心呼叫該函式完成寫操作。寫入函式在向記憶體中寫入資料之前,必須首先檢查VFS索引節點中的資訊,同時滿足如下條件時,才能進行實際的記憶體複製工作:

·記憶體中有足夠的空間可容納所有要寫入的資料;

·記憶體沒有被讀程式鎖定。

如果同時滿足上述條件,寫入函式首先鎖定記憶體,然後從寫程序的地址空間中複製資料到記憶體。否則,寫入程序就休眠在VFS索引節點的等待佇列中,接下來,核心將呼叫排程程式,而排程程式會選擇其他程序執行。寫入程序實際處於可中斷的等待狀態,當記憶體中有足夠的空間可以容納寫入資料,或記憶體被解鎖時,讀取程序會喚醒寫入程序,這時,寫入程序將接收到訊號。當資料寫入記憶體之後,記憶體被解鎖,而所有休眠在索引節點的讀取程序會被喚醒。

管道的讀取過程和寫入過程類似。但是,程序可以在沒有資料或記憶體被鎖定時立即返回錯誤資訊,而不是阻塞該程序,這依賴於檔案或管道的開啟模式。反之,程序可以休眠在索引節點的等待佇列中等待寫入程序寫入資料。當所有的程序完成了管道操作之後,管道的索引節點被丟棄,而共享資料頁也被釋放。

因為管道的實現涉及很多檔案的操作

獲取Linux 記憶體頁(基頁)大小的命令:getconf PAGE_SIZE,一般的輸出是4096,即 4KB。

wKioL1eMw_LQ5yg6AAAK_k93Gqc881.png

管道的容量


wKiom1eMxAzx35YiAAA7meBQhxY403.png

wKioL1eMxAzBa_QqAABM6n2nHs4172.png

wKiom1eMxAzSX4WNAABhK-9__6Y531.png

wKiom1eMxA2wBjAAAAASZ2BjLt8717.png

【64KB】

管道的特點:


1 依賴檔案系統

2 單向通訊,要想雙向,需要建立兩個管道

3 允許有血緣關係的程序進行通訊

4 面向位元組流

5 管道隨程序,程序在管道在,程序消失管道對應的埠關閉,兩個程序都消失則管道消失。

管道的四種特殊情況:


情況1: pipe寫端關閉 讀端一直讀 ,直到剩餘資料都讀完,最後一次read返回0,就像讀到檔案末尾一樣。

測試程式碼:

wKiom1eMxYeDaW3BAABZDQtGv2E471.png

wKiom1eMxYewuYQPAAA9VHkvnAg248.png

wKioL1eMxYihSF9EAAAQLjakg4c708.png



情況2:寫端沒有關閉,但寫端也不寫入資料,讀端一直讀,讀完資料後再次read會阻塞,直到有資料時再讀。

wKiom1eMxaTQ3Mo1AAAq9Prw44I624.png


情況3:讀端關閉,寫端write,那麼程序會收到訊號SIGPIPE,通常導致程序 異常終止

wKioL1eMxcrCtxPPAAHcuyvxCbw254.png

wKiom1eMxcqyQJnQAADNx6JvQbg806.png

情況4:讀端沒有關閉,讀端也沒有讀

寫端一直寫,管道被寫滿時再write會阻塞,直到管道中有空位置了才寫入。

wKioL1eMxfnC6WHFAADHIAFGgm4159.png

wKiom1eMxfrRISL9AAAM6eOhWy0377.png

阻塞:

wKiom1eMxfqDstYsAAAq2ArXG0g678.png


轉載於:https://blog.51cto.com/alick/1827499