Linux下的管道pipe----管道容量和實現機制

  管道容量 

   linux上的PipE容量爲(capacity)65536個字節;實驗得到Ubuntu的PIPE_BUF爲4096。

   當 管道的寫端存在時,如果請求的字節數目大於PIPE_BUF,則返回管道中現有的數據字節數,如果請求的字節數目不大於PIPE_BUF,則返回管道中現有數據字節數(此時,管道中數據量小於請求的數據量);或者返回請求的字節數(此時,管道中數據量不小於請求的數據量)(PIPE_BUF在 include/linux/limits.h中定義,不同的內核版本可能會有所不同。Posix.1要求PIPE_BUF至少爲512字節,red hat 7.2中爲4096字節)

要保證write操作是原子操作,一次寫入的字節數n就不能超過PIPE_BUF;非阻塞模式下當然還需要有>=n的空餘空間,否則寫入失敗。一次寫入大於PIPE_BUF的數據,不管是不是阻塞模式都不保證是原子操作

非阻塞模式下,能一次性寫入的最大數據量就是管道的容量即65536字節了,注意這裏的一次性寫入是指write不返回的情況下,和原子操作不是一回事。如果寫入的更長,也只能寫這麼多,write函數會返回已寫入的長度

如果寫端不存在,讀的時候會返回0,代表讀到了文件尾。如果讀端不存在,寫的時候就會產生SIGPIPE信號(忽略此信息的情況下write會返回失敗,errno爲EPIPE)

在linux終端輸入“man 7 pipe”命令,可以查找到管道容量的最大和最小字節數


管道實現機制

(一)管道外部實現
當我們定義一個管道時,這個管道是由內核管理的一個緩衝區,可以抽象爲現實生活中的一個傳輸線路。管道的一端連接一個進程的輸出,這個進程會向管道中放入信息。管道的另一端連接一個進程的輸入,這個進程取出被放入管道的信息。當管道中沒有信息的話,從管道中讀取的進程會等待,直到另一端的進程放入信息。當管道被放滿信息的時候,嘗試放入信息的進程會等待,直到另一端的進程取出信息。當兩個進程都終結的時候,管道也自動消失。 從原理上,管道利用fork機制建立,從而讓兩個進程可以連接到同一個PIPE上。
Linux中,管道是一種使用非常頻繁的通信機制。從本質上說,管道也是一種文件,但它又和一般的文件有所不同,管道可以克服使用文件進行通信的兩個問題,具體表現爲:
· (1)限制管道的大小。實際上,管道是一個固定大小的緩衝區。在Linux中,該緩衝區的大小爲1頁,即4K字節,使得它的大小不象文件那樣不加檢驗地增長。使用單個固定緩衝區也會帶來問題,比如在寫管道時可能變滿,當這種情況發生時,隨後對管道的write()調用將默認地被阻塞,等待某些數據被讀取,以便騰出足夠的空間供write()調用寫。
· (2) 讀取進程也可能工作得比寫進程快。當所有當前進程數據已被讀取時,管道變空。當這種情況發生時,一個隨後的read()調用將默認地被阻塞,等待某些數據被寫入,這解決了read()調用返回文件結束的問題。
(二)管道的內部實現機制


 實際上pipe並沒有單獨的實現數據結構,他利用了文件的在 Linux 中,而是藉助了文件系統的file結構和VFS的索引節點inode。通過將兩個 file 結構指向同一個臨時的 VFS 索引節點,而這個 VFS 索引節點又指向一個物理頁面而實現的。有兩個 file 數據結構,但它們定義文件操作例程地址是不同的,其中一個是向管道中寫入數據的例程地址,而另一個是從管道中讀出數據的例程地址。這樣,用戶程序的系統調用仍然是通常的文件操作,而內核卻利用這種抽象機制實現了管道這一特殊操作。
(三)管道的容量
當管道一端不斷地讀取數據,另一端卻不輸出數據。根據linux的實現機制當管道讀滿是輸出端自動阻塞。所以這個管道是有大小的
現在我們編寫程序來測試管道的大小

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. #include <unistd.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4.   
  5. int main(int argc, char* argv[])  
  6. {  
  7.     int pipefds[2]; //[0] for read, [1] for write  
  8.     pipe(pipefds);  
  9.       
  10.     char buf[4096];  
  11.     for (int i = 0; i < sizeof(buf); ++i)  
  12.     {  
  13.         buf[i] = 0x7f;  
  14.     }  
  15.       
  16.     ssize_t ret = -1;  
  17.       
  18.     int loop = 100;  
  19.     if (argc > 1)  
  20.     {  
  21.         loop = atoi(argv[1]);  
  22.     }  
  23.       
  24.     for (int i = 0; i < loop; ++i)  
  25.     {  
  26.         printf("loop: %d\n", i);  
  27.         ret = write(pipefds[1], buf, sizeof(buf));  
  28.         if (ret < 0)  
  29.         {  
  30.             perror(NULL);  
  31.         }  
  32.         else  
  33.         {  
  34.             printf("%d\n", ret);  
  35.         }  
  36.     } // 當i=16的時候會阻塞,可知管道大小爲64k  
  37.       
  38.     close(pipefds[0]);  
  39.     close(pipefds[1]);  
  40.       
  41.     return 0;  
  42. }  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章