我會用幾篇博客總結一下在Linux中進程之間通信的幾種方法,我會把這個開頭的摘要部分在這個系列的每篇博客中都打出來
進程之間通信的方式
- 管道
- 消息隊列
- 信號
- 信號量
- 共享存儲區
- 套接字(socket)
進程間通信(二)—消息隊列傳送門:http://www.cnblogs.com/lenomirei/p/5642575.html
進程間通信(三)—信號量傳送門:http://www.cnblogs.com/lenomirei/p/5649792.html
進程間通信(四)—共享存儲區傳送門:http://www.cnblogs.com/lenomirei/p/5651995.html
進程間通信(五)—信號傳送門:http://www.cnblogs.com/lenomirei/p/5656449.html
在以一切皆文件為原則的Linux系統中,管道也是一種文件(特殊文件),可以使用mkfifo命令創建一個管道文件

在管道文件的前面有一個p來標識管道文件
這次主要說的是通過管道完成進程之間的通信,通過管道通信有兩種方式。
一種是匿名管道,一種是命名管道
- 匿名管道
先來看一段代碼
1 #define MAXLINE 80
2 int main() 3 { 4 int n; 5 int fd[2]; 6 pid_t pid; 7 char line[MAXLINE]; 8 if (pipe(fd) < 0) 9 perror("pipe"); 10 if ((pid = fork()) < 0) 11 perror("fork"); 12 if (pid > 0) 13 { 14 //father
15 close(fd[0]); 16 } 17 else
18 { 19 close(fd[1]); 20 n = read(fd[0], line, MAXLINE); 21 write(stdout, line, n);//寫到標准輸出上看一下效果
22 } 23 return 0; 24
25 }
這個程序就是一個簡單的父子進程之間通過管道進行通信的一個例子,具體的工作過程我用畫圖的方式展現出來



注意這一個步驟是十分重要的,如果不關閉相應的端口,就無法正確操作管道。
匿名管道主要利用了,創建子進程的時候會把父進程的文件描述符表拷貝一份這個特征,通過這個特征,父子進程就看到了一個公共的資源—管道,並同時擁有對該管道腹瀉的權利,那么一方讀,一方寫,就可以完成進程之間的通信了。
所謂的匿名管道就是說,沒有名字。。。你根本不知道這個管道文件存放在哪,也不知道這個管道文件的文件名,唯一跟這個跟管道文件有聯系的只有父子進程中的文件描述符。那么根據匿名管道的實現機制,很容易就能看出他的優缺點。
- 管道的n個特征
- 管道是依賴於文件系統的,創建好管道之后,一定要關閉不使用的讀寫端
- 只有父子進程才可以使用管道通信,也就是所謂的有血緣關系的進程進行進程間通信。(匿名管道獨有)
- 管道是基於數據流的,面向字節流!(后面會提到消息隊列是面向數據塊的,對比來看會好懂一些)
- 管道只能稱之為單向數據通信,連半雙工都算不上
- 同步與互斥問題不需要考慮了,管道已經考慮了
- 當父子進程退出的時候,管道的生命周期就結束了,也就是說管道的生命周期就是進程
上述就是匿名管道的使用和實現機制,可以看出必須有“親緣關系”的進程之間才可以使用匿名管道來完成進程間通信。父子進程當然可以,“孫子”進程也是可以的~
那么為了解決只有有親緣關系的進程才能使用這種方式進行通信的弊端,就有了命名管道的通信方式
- 命名管道
簡單的來說,我們剛才使用的匿名管道是因為不知道文件名和存放路徑,所以只能通過繼承文件描述符表來獲得跟匿名管道建立聯系的方式,如果我們知道路徑和管道文件名呢?那不就可以完成非親緣關系的進程間通信了么
- 函數原型 int mkfifo(const char *pathname,mode_t mode)
- 其中第一個參數pathname就是路徑了,mode 就是創建的管道的訪問權限umask
- 頭文件:#include <sys.stat.h> #include <sys/types.h>
- 返回值,成功返回0,失敗返回-1
下面貼上代碼
這個是server的
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 #include <errno.h>
7 #include <string.h>
8 #include <fcntl.h>
9 #define _PATH_NAME_ "./pp"
10
11 int main() 12 { >> 13 char *str="hello world"; 14 int fd; 15 if(mkfifo(_PATH_NAME_,0644)<0)· 16 { 17 printf("hahaha\n"); 18 printf("mkfifo error %d:%s\n",errno,strerror(errno)); 19 } 20 fd=open(_PATH_NAME_,O_WRONLY); 21 write(fd,str,strlen(str)); 22 return 0; 23 }
這個是client的
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <stdio.h>
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include <errno.h>
8 #include <string.h>
9 #include <fcntl.h>
10
11 #define _PATH_NAME_ "./pp"
12
13
14 int main() 15 { 16 int fd; 17 fd=open(_PATH_NAME_,O_RDONLY); 18 char buf[1024]={'\0'}; 19 read(fd,buf,1024); 20 printf("%s\n",buf); 21 return 0; 22 }
可以通過open函數來打開分別使用read和write函數來讀寫管道,當然也可以close掉標准輸入輸出,做成重定向的那樣也是可以的
那么管道有多大呢,我們可以往里面扔多少數據呢?
既然管道也是一個文件,那么肯定有大小上限,這也是其一個缺點,大小有限的,那么我們究竟可以寫多少數據放進管道呢,使用man 7 pipe命令打開pipe的說明文檔,可以看見這么一段
Pipe capacity
A pipe has a limited capacity. If the pipe is full, then a write(2)
will block or fail, depending on whether the O_NONBLOCK flag is set
(see below). Different implementations have different limits for the
pipe capacity. Applications should not rely on a particular capacity:
an application should be designed so that a reading process consumes
data as soon as it is available, so that a writing process does not
remain blocked.
In Linux versions before 2.6.11, the capacity of a pipe was the same as
the system page size (e.g., 4096 bytes on i386). Since Linux 2.6.11,
the pipe capacity is 65536 bytes.
看,我現在的版本是2.6.11之后了,所以是65536字節,之間的版本就是4096字節
最后的最后,來一個管道最大的缺點,管道是基於文件系統的!所以不管是讀還是寫,都要求訪問磁盤進行I/O操作,I/O的速度你懂得,特別慢,所以不適合做多個client的結構,不然會很慢
