進程間通信方式主要分為 管道、SystemV IPC、 POSIX IPC三大類,管道作為進程間通信的一大重要方式,平時應用當中十分廣泛。於是這里就先簡單整理了一些關於管道的用法和注意事項。
匿名管道
管道是UNIX中最古老的進程間通信形式。通常將一個進程連接到另一個進程的一個數據流稱為一個 “管道”。它本質上其實就是內核的一塊緩存。
管道的限制:
* 大小有限制(一般是65536)
*半雙工 (數據只能向一個方向流動)
*在有親緣關系的進程間使用(父進程創建一個管道,兩個子進程通過管道進行通信也行)
如何創建管道?
使用 int pipe(int fds[2]) 函數創建一個無名管道 //fds[0]代表讀; fds[1] 代表寫 ; 函數成功返回0, 錯誤返回錯誤代碼
比如來創建一個管道來用於父子進程間通信:

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 6 //父子進程間通信 7 int main(void) 8 { 9 int fds[2]; 10 //創建一個無名管道 11 if(pipe(fds) == -1){ 12 perror("make pipe");exit(1); 13 } 14 15 pid_t pid = fork(); 16 if(pid == 0) 17 { //子進程寫 18 close(fds[0]); //關閉讀 19 //sleep(1); 20 write(fds[1], "change world!", 14); 21 close(fds[1]); 22 } 23 else 24 { //父進程讀 25 close(fds[1]); //關閉寫 26 char buf [100] = {}; 27 int r = read(fds[0], buf, 100); //返回讀取的字節數 28 printf("r= %d, buf=%s\n", r, buf); 29 close(fds[0]); 30 } 31 32 }過關到
再來分析分析 ls -l | wc -l 這種采用了管道的命令,假定子進程實現ls,父進程實現wc。ls命令正常執行將結果集寫出到stdout,但dup后,現在會寫入管道的寫端;wc –l 正常應該從stdin讀取數據,dup后會從管道的fd[0]讀。
來代碼:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> int main(void) { int fds[2]; if(pipe(fds) < 0){
perror("make pipe");exit(1);
}; pid_t pid = fork(); if(pid == 0) { //子進程寫進管道 執行ls -l close(1); close(fds[0]); dup(fds[1]); //讓stdout指向fds[1]指向的file表 execlp("ls", "ls", "-l", NULL); //實現將ls -l的結果寫入管道 close(fds[1]); exit(1); } else { //父進程從管道讀 執行wc -l close(0); close(fds[1]); dup(fds[0]); //讓stdin指向fds[0]指向file表 close(fds[0]); execlp("wc", "wc", "-l", NULL); //從管道接收數據,執行wc -l命令 exit(1); } }
注意:不能讓父進程執行 ls -l ,子進程執行 wc -l 。這是因為程序的子進程將stdin重定向給管道,父進程執行ls -l 的結果集將通過管道寫給子進程。若父進程在子進程打印wc的結果到屏幕之前被shell調用wait回收,shell就會先輸出$提示符,最后程序都執行結束,shell還在阻塞等待用戶輸入。
總結:
(1) 讀管道:
1.管道中有數據,read返回實際讀到的字節數。
2.管道中無數據:
①管道寫端被全部關閉,read返回0 (好像讀到文件結尾)
② 寫端沒有全部被關閉,read阻塞等待(不久的將來可能有數據抵達,此時會讓出cpu資源)
(2) 寫管道:
1. 管道讀端全部被關閉, 進程異常終止 (操作系統發出SIGPIPE信號)
2. 管道讀端沒有全部關閉:
①管道已滿,write阻塞。
②管道未滿,write將數據寫入,並返回實際寫入的字節數。
命名管道
命名管道可在同一台計算機的不同進程之間或在跨越一個網絡的不同計算機的不同進程之間,支持可靠的、單向或雙向的數據通信。
不同於匿名管道的是:命名管道可以在不相關的進程之間和不同計算機之間使用,服務器建立命名管道時給它指定一個名字,任何進程都可以通過該名字打開管道的另一端,根據給定的權限和服務器來進程通信。使通過網絡傳輸數據並不比同一計算機上兩進程之間通信更困難,不過如果要同時和多個進程通信它就力不從心了。
如何創建?
int mkfifo(const char* pathname, mode_t mod)

//命名管道創建 #include <stdio.h> #include <sys/stat.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> int main(void) { if(mkfifo("fifo_pipe", 0777) == -1) { perror("make pipe"); exit(1); } return 0; }
read.c

#include<stdio.h> #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> int main(void) { int fd; char buf[80]; fd = open("fifo_pipe", O_RDONLY); while(1) { read(fd, buf, sizeof(buf)); //讀到buf里 printf("%s\n", buf); sleep(1); } return 0; }
write.c

1 #include<sys/types.h> 2 #include<sys/stat.h> 3 #include<stdio.h> 4 #include<unistd.h> 5 #include<fcntl.h> 6 7 int main(void) 8 { 9 int fd; 10 char s[]="I am from write.c"; 11 fd=open("fifo_pipe", O_WRONLY); 12 13 while(1) 14 { 15 write(fd, s, sizeof(s)); 16 sleep(1); 17 } 18 19 return 0; 20 } ~
注意:一旦建立起聯系,刪掉管道名稱文件進程間的通信也不會出現問題。