天天看點

UNIX(程序間通信):09 管道到底是什麼

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

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

Linux管道的實作機制

        在Linux中,管道是一種使用非常頻繁的通信機制。從本質上說,管道也是一種檔案,但它又和一般的檔案有所不同,實際上比如我們看一個有名管道檔案的屬性如下:

UNIX(程式間通信):09 管道到底是什麼

這個filename是在上一節講到的有名管道檔案,可以看到檔案類型是p(管道檔案)。

這裡對于Linux的檔案屬性不熟悉的技術同學來說,可以看下這個

● d 目錄檔案directory

● b 塊裝置blocks

● c字元裝置character

● |符号連結檔案link

●p管道檔案pipe

●s套接字檔案socket

       管道可以克服使用檔案進行通信的兩個問題,具體表現為:

·      限制管道的大小。實際上,管道是一個固定大小的緩沖區。在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中的代碼,你會覺得并不難了解。

int fd1[2];
         if(pipe(fd1))
        { printf("pipe() FAILED: errno=%d",errno);
              return 1;
         }      

繼續閱讀