管道容量
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的實現機制當管道讀滿是輸出端自動阻塞。所以這個管道是有大小的
現在我們編寫程序來測試管道的大小
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- int main(int argc, char* argv[])
- {
- int pipefds[2]; //[0] for read, [1] for write
- pipe(pipefds);
- char buf[4096];
- for (int i = 0; i < sizeof(buf); ++i)
- {
- buf[i] = 0x7f;
- }
- ssize_t ret = -1;
- int loop = 100;
- if (argc > 1)
- {
- loop = atoi(argv[1]);
- }
- for (int i = 0; i < loop; ++i)
- {
- printf("loop: %d\n", i);
- ret = write(pipefds[1], buf, sizeof(buf));
- if (ret < 0)
- {
- perror(NULL);
- }
- else
- {
- printf("%d\n", ret);
- }
- } // 當i=16的時候會阻塞,可知管道大小爲64k
- close(pipefds[0]);
- close(pipefds[1]);
- return 0;
- }