進程間通訊方式主要分爲 管道、SystemV IPC、 POSIX IPC三大類,管道做爲進程間通訊的一大重要方式,平時應用當中十分普遍。因而這裏就先簡單整理了一些關於管道的用法和注意事項。shell
管道是UNIX中最古老的進程間通訊形式。一般將一個進程鏈接到另外一個進程的一個數據流稱爲一個 「管道」。它本質上其實就是內核的一塊緩存。緩存
管道的限制:服務器
* 大小有限制(通常是65536)網絡
*半雙工 (數據只能向一個方向流動)ide
*在有親緣關係的進程間使用(父進程建立一個管道,兩個子進程經過管道進行通訊也行)函數
如何建立管道?spa
使用 int pipe(int fds[2]) 函數建立一個無名管道 //fds[0]表明讀; fds[1] 表明寫 ; 函數成功返回0, 錯誤返回錯誤代碼操作系統
好比來建立一個管道來用於父子進程間通訊:3d
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]讀。code
來代碼:
#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 } ~
注意:一旦創建起聯繫,刪掉管道名稱文件進程間的通訊也不會出現問題。