Linux管道的實現機制

Linux管道的實現機制

在Linux中,管道是一種使用很是頻繁的通訊機制。從本質上說,管道也是一種文件,但它又和通常的文件有所不一樣,管道能夠克服node

使用文件進行通訊的兩個問題,具體表現爲:數據結構

·      限制管道的大小。實際上,管道是一個固定大小的緩衝區。在Linux中,該緩衝區的大小爲1頁,即4K字節,使得它的大小不象文函數

件那樣不加檢驗地增加。使用單個固定緩衝區也會帶來問題,好比在寫管道時可能變滿,當這種狀況發生時,隨後對管道的write()調spa

用將默認地被阻塞,等待某些數據被讀取,以便騰出足夠的空間供write()調用寫。索引

·      讀取進程也可能工做得比寫進程快。當全部當前進程數據已被讀取時,管道變空。當這種狀況發生時,一個隨後的read()調用將隊列

默認地被阻塞,等待某些數據被寫入,這解決了read()調用返回文件結束的問題。進程

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

1. 管道的結構

     在 Linux 中,管道的實現並無使用專門的數據結構,而是藉助了文件系統的file結構和VFS的索引節點inode。經過將兩個 file 結內存

構指向同一個臨時的VFS 索引節點,而這個 VFS 索引節點又指向一個物理頁面而實現的。如圖 7.1所示。同步

  

圖7.1  管道結構示意圖

圖7.1中有兩個 file 數據結構,但它們定義文件操做例程地址是不一樣的,其中一個是向管道中寫入數據的例程地址,而另外一個是從管

道中讀出數據的例程地址。這樣,用戶程序的系統調用仍然是一般的文件操做,而內核卻利用這種抽象機制實現了管道這一特殊操

做。

2.管道的讀寫

      管道實現的源代碼在fs/pipe.c中,在pipe.c中有不少函數,其中有兩個函數比較重要,即管道讀函數pipe_read()和管道寫函數

pipe_wrtie()。管道寫函數經過將字節複製到 VFS 索引節點指向的物理內存而寫入數據,而管道讀函數則經過複製物理內存中的字節

而讀出數據。固然,內核必須利用必定的機制同步對管道的訪問,爲此,內核使用了鎖、等待隊列和信號。

     當寫進程向管道中寫入時,它利用標準的庫函數write(),系統根據庫函數傳遞的文件描述符,可找到該文件的 file 結構。file 結構

中指定了用來進行寫操做的函數(即寫入函數)地址,因而,內核調用該函數完成寫操做。寫入函數在向內存中寫入數據以前,必

須首先檢查 VFS 索引節點中的信息,同時知足以下條件時,才能進行實際的內存複製工做:

內存中有足夠的空間可容納全部要寫入的數據;

內存沒有被讀程序鎖定。

若是同時知足上述條件,寫入函數首先鎖定內存,而後從寫進程的地址空間中複製數據到內存。不然,寫入進程就休眠在 VFS 索引

節點的等待隊列中,接下來,內核將調用調度程序,而調度程序會選擇其餘進程運行。寫入進程實際處於可中斷的等待狀態,當內

存中有足夠的空間能夠容納寫入數據,或內存被解鎖時,讀取進程會喚醒寫入進程,這時,寫入進程將接收到信號。當數據寫入內

存以後,內存被解鎖,而全部休眠在索引節點的讀取進程會被喚醒。

     管道的讀取過程和寫入過程相似。可是,進程能夠在沒有數據或內存被鎖定時當即返回錯誤信息,而不是阻塞該進程,這依賴

文件或管道的打開模式。反之,進程能夠休眠在索引節點的等待隊列中等待寫入進程寫入數據。當全部的進程完成了管道操做之

後,管道的索引節點被丟棄,而共享數據頁也被釋放。

   由於管道的實現涉及不少文件的操做,所以,當讀者學完有關文件系統的內容後來讀pipe.c中的代碼,你會以爲並不難理解。

相關文章
相關標籤/搜索