管道是UNIX系統IPC的最古老的形式,並且所有UNIX系統都提供此種通信機制。但是管道存在如下特點:
- 管道是半雙工的。
- 管道只能用在具有公共祖先的進程之間。
#include <unistd.h>
int pipe(int filedes[2]);
參數filedes[2]是兩個文件描述符,filedes[0]爲讀打開,filedes[1]爲寫打開。filedes[1]的輸出是filedes[0]的輸入。這個進程會向管道中放入信息。管道的另一端連接一個進程的輸入,這個進程取出被放入管道的信息。
一個緩衝區不需要很大,它被設計成爲環形的數據結構,以便管道可以被循環利用。
當管道中沒有信息的話,從管道中讀取的進程會等待,直到另一端的進程放入信息。
當管道被放滿信息的時候,嘗試放入信息的進程會等待,直到另一端的進程取出信息。當兩個進程都終結的時候,管道也自動消失。
最開始的時候,上面的兩個箭頭都連接在同一個進程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。
- 對於管道的關閉:
關閉寫端口是給讀端口一個文件結束符的唯一方法。對於寫端口關閉後,在該管道上的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
管道的侷限性管道的主要侷限性正體現在它的特點上:
- 只支持單向數據流;
- 只能用於具有親緣關係的進程之間;
- 沒有名字;
- 管道的緩衝區是有限的(管道制存在於內存中,在管道創建時,爲緩衝區分配一個頁面大小);
- 管道所傳送的是無格式字節流,這就要求管道的讀出方和寫入方必須事先約定好數據的格式,比如多少字節算作一個消息(或命令、或記錄)等等。