歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux中的pipe與named pipe(FIFO),即管道和命名管道

Linux中的pipe與named pipe(FIFO),即管道和命名管道

日期:2017/2/28 16:15:00   编辑:Linux教程

管道是Linux中很重要的一種通信方式,是把一個程序的輸出直接連接到另一個程序的輸入,常說的管道多是指無名管道,無名管道只能用於具有親緣關系的進程之間,這是它與有名管道的最大區別。

有名管道叫named pipe或者FIFO(先進先出),可以用函數mkfifo()創建。

FIFO

FIFO就是命名管道,或有名管道。對於pipe我們不難看出它只能用於一個進程家族之間通信,父子之間,兄弟之間等等。如果想要讓管道在更寬泛的環境中,那是不行的,原因是它沒有“名字”或者說是匿名的,另外的進程看不到它,這樣就有了命名管道。它同樣是基於VFS,對應的文件類型就是FIFO文件,可以通過mknod命令在磁盤上創建一個FIFO文件(注意:這就是它與pipe的本質區別,pipe完全就是存在與內存中,在磁盤上毫無痕跡),當進程想通過該FIFO來通信時就可以標准的API open打開該文件,然後開始讀寫操作。對於FIFO的讀寫實現,它與pipe是相同的。區別在於,FIFO有open這一操作,而pipe是在調用pipe這個系統調用時直接創建了一對文件描述符用於通信。並且,FIFO的open操作還有些細致的地方要考慮,例如如果寫者先打開,尚無讀者,那麼肯定是不能通信了,所以就需要先去睡眠等待讀者打開該FIFO,反之對讀者亦然。

管道是Linux中很重要的一種通信方式,是把一個程序的輸出直接連接到另一個程序的輸入,常說的管道多是指無名管道,無名管道只能用於具有親緣關系的進程之間,這是它與有名管道的最大區別。

有名管道叫named pipe或者FIFO(先進先出),可以用函數mkfifo()創建。

Linux管道的實現機制

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

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

· 讀取進程也可能工作得比寫進程快。當所有當前進程數據已被讀取時,管道變空。當這種情況發生時,一個隨後的read()調用將默認地被阻塞,等待某些數據被寫入,這解決了read()調用返回文件結束的問題。

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

1. 管道的結構

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

2.管道的讀寫

管道實現的源代碼在fs/pipe.c中,在pipe.c中有很多函數,其中有兩個函數比較重要,即管道讀函數pipe_read()和管道寫函數 pipe_wrtie()。管道寫函數通過將字節復制到 VFS 索引節點指向的物理內存而寫入數據,而管道讀函數則通過復制物理內存中的字節而讀出數據。當然,內核必須利用一定的機制同步對管道的訪問,為此,內核使用了鎖、等待隊列和信號。

當寫進程向管道中寫入時,它利用標准的庫函數write(),系統根據庫函數傳遞的文件描述符,可找到該文件的 file 結構。file 結構中指定了用來進行寫操作的函數(即寫入函數)地址,於是,內核調用該函數完成寫操作。寫入函數在向內存中寫入數據之前,必須首先檢查 VFS 索引節點中的信息,同時滿足如下條件時,才能進行實際的內存復制工作:


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

·內存沒有被讀程序鎖定。

如果同時滿足上述條件,寫入函數首先鎖定內存,然後從寫進程的地址空間中復制數據到內存。否則,寫入進程就休眠在 VFS 索引節點的等待隊列中,接下來,內核將調用調度程序,而調度程序會選擇其他進程運行。寫入進程實際處於可中斷的等待狀態,當內存中有足夠的空間可以容納寫入數據,或內存被解鎖時,讀取進程會喚醒寫入進程,這時,寫入進程將接收到信號。當數據寫入內存之後,內存被解鎖,而所有休眠在索引節點的讀取進程會被喚醒。

管道的讀取過程和寫入過程類似。但是,進程可以在沒有數據或內存被鎖定時立即返回錯誤信息,而不是阻塞該進程,這依賴於文件或管道的打開模式。反之,進程可以休眠在索引節點的等待隊列中等待寫入進程寫入數據。當所有的進程完成了管道操作之後,管道的索引節點被丟棄,而共享數據頁也被釋放。

因為管道的實現涉及很多文件的操作,因此,當讀者學完有關文件系統的內容後來讀pipe.c中的代碼,你會覺得並不難理解。

Linux 管道的創建和使用都要簡單一些,唯一的原因是它需要更少的參數。實現與 Windows 相同的管道創建目標,Linux 和 UNIX 使用下面的代碼片段:

  創建 Linux 命名管道
  int fd1[2];

if(pipe(fd1))

{ printf("pipe() FAILED: errno=%d",errno);

return 1;

}

Copyright © Linux教程網 All Rights Reserved