Linux進程間通信之管道(pipe)

管道是UNIX系統IPC的最古老的形式,並且所有UNIX系統都提供此種通信機制。但是管道存在如下特點:

  1. 管道是半雙工的。
  2. 管道只能用在具有公共祖先的進程之間。
管道的創建
管道是通過調用pipe函數創建的:
#include <unistd.h>

int pipe(int filedes[2]);
參數filedes[2]是兩個文件描述符,filedes[0]爲讀打開,filedes[1]爲寫打開。filedes[1]的輸出是filedes[0]的輸入。

管道的實現機制
管道是由內核管理的一個緩衝區,相當於我們放入內存中的一個紙條。管道的一端連接一個進程的輸出。
這個進程會向管道中放入信息。管道的另一端連接一個進程的輸入,這個進程取出被放入管道的信息。
一個緩衝區不需要很大,它被設計成爲環形的數據結構,以便管道可以被循環利用。
當管道中沒有信息的話,從管道中讀取的進程會等待,直到另一端的進程放入信息。
當管道被放滿信息的時候,嘗試放入信息的進程會等待,直到另一端的進程取出信息。當兩個進程都終結的時候,管道也自動消失。

從原理上,管道利用fork機制建立,從而讓兩個進程可以連接到同一個PIPE上。
最開始的時候,上面的兩個箭頭都連接在同一個進程Process 1上(連接在Process 1上的兩個箭頭)。當fork複製進程的時候,會將這兩個連接也複製到新的進程(Process 2)。
隨後,每個進程關閉自己不需要的一個連接 (兩個黑色的箭頭被關閉; Process 1關閉從PIPE來的輸入連接,Process 2關閉輸出到PIPE的連接).
這樣,剩下的紅色連接就構成了如上圖的PIPE。

管道的讀寫
  •  對於寫管道:
  寫入管道的數據按到達次序排列。如果管道滿,則對管道的寫被阻塞,直到管道的數據被讀操作讀取。
        對於寫操作,如果一次write調用寫的數據量小於管道容量,則寫必須一次完成,
        即如果管道所剩餘的容量不夠,write被阻塞直到管道的剩餘容量可以一次寫完爲止。如果write調用寫的數據量大於管道容量,則寫操作分多次完成。
        如果用fcntl設置管道寫端口爲非阻塞方式,則管道滿不會阻塞寫,而只是對寫返回0。
  • 對於讀管道:
  讀操作按數據到達的順序讀取數據。已經被讀取的數據在管道內不再存在,這意味着數據在管道中不能重複利用。
        如果管道爲空,且管道的寫端口是打開狀態,則讀操作被阻塞直到有數據寫入爲止。
        一次read調用,如果管道中的數據量不夠read指定的數量,則按實際的數量讀取,並對read返回實際數量值。
        如果讀端口使用fcntl設置了非阻塞方式,則當管道爲空時,read調用返回0。
  • 對於管道的關閉:
  如果管道的讀端口關閉,那麼在該管道上的發出寫操作調用的進程將接收到一個SIGPIPE信號。
        關閉寫端口是給讀端口一個文件結束符的唯一方法。對於寫端口關閉後,在該管道上的read調用將返回0。

管道應用實例

  • 實例一:用於shell
            管道可用於輸入輸出重定向,它將一個命令的輸出直接定向到另一個命令的輸入。
            比如,當在某個shell程序鍵入who│wc -l後,相應shell程序將創建who以及wc兩個進程和這兩個進程間的管道。
  • 實例二:用於具有親緣關係的進程間通信
             下面的程序創建了一個從父進程到子進程的管道。並且父進程經由管道向子進程傳送數據。
  1 #include <stdlib.h>
  2 #include <stdio.h>
  3 #include <unistd.h>
  4 #include <signal.h>
  5 #include <string.h>
  6 
  7 #define MAXLEN  128
  8 
  9 int main()
 10 {
 11     int ret = 0;
 12     int filedes[2] = {-1, -1};
 13     char buf[MAXLEN];
 14 
 15     ret = pipe(filedes);
 16     if (ret == -1) {
 17         perror("create pipe error");
 18         return -1;
 19     }
 20 
 21     ret = fork();
 22     if (ret < 0) {  // fork error
 23         perror("fork error");
 24         return -1;
 25     } else if (ret > 0) {   // parent process
 26         close(filedes[0]);
 27 
 28         write(filedes[1], "hello world\n", 12);
 29         
 30     } else {    // child process
 31         close(filedes[1]);
 32 
 33         ret = read(filedes[0], buf, MAXLEN);
 34         if (ret < 0) {
 35             perror("read pipe error");
 36             return -1;
 37         }
 38         write(STDOUT_FILENO, buf, ret);
 39     }
 40 
 41     return 0;
 42 }
程序執行結果如下:
[shenlx@CentOS pipe]$ ./a.out 
hello world
管道的侷限性
管道的主要侷限性正體現在它的特點上:
  • 只支持單向數據流;
  • 只能用於具有親緣關係的進程之間;
  • 沒有名字;
  • 管道的緩衝區是有限的(管道制存在於內存中,在管道創建時,爲緩衝區分配一個頁面大小);
  • 管道所傳送的是無格式字節流,這就要求管道的讀出方和寫入方必須事先約定好數據的格式,比如多少字節算作一個消息(或命令、或記錄)等等。

發佈了33 篇原創文章 · 獲贊 6 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章