Linux間進程通信的方法在前一篇文章中已有詳細介紹。http://blog.csdn.net/jmy5945hh/article/details/7350564
本篇詳細介紹及代碼測試第一種方式,即管道(Pipe)及有名管道(named pipe)。
1-1 管道簡介
也稱匿名管道,其在系統中沒有實名,是進程的一種資源,因此不可以在文件系統中以任何形式查看。
生存週期從被創建開始,到進程結束或進程主動關閉管道。
數據在管道之間以無格式流式傳遞(想象爲水在管道里流動)。
管道是一種半雙工通信方式,因此通常需要建立起兩根管道以完成收發工作。遵從FIFO原則。
優點是使用方便簡單,缺點是功能上有很多限制,且只能作爲父子進程間通信手段。
典型應用:linux SHELL命令的管道連接。例如:
- kill -l | grep SIGINT
1-2 相關函數
創建管道:
- #include <unistd.h>
- int pipe(int fd[2]);
成功後返回0;出錯返回-1。fd是文件描述符數組。
讀寫管道:
使用文件I/O的write和read函數,參考http://blog.csdn.net/jmy5945hh/article/details/7465879
關閉管道:
使用close函數,參考http://blog.csdn.net/jmy5945hh/article/details/7465879
1-3 管道測試代碼
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <errno.h>
- #include <string.h>
- #define MAX 4096
- int main(int argc, char *argv[]) {
- int pipefd[2], recvBytes, sendBytes;
- pid_t pid;
- unsigned char sendBuf[MAX], recvBuf[MAX];
- memset(sendBuf, 0x00, MAX);
- memset(recvBuf, 0x00, MAX);
- if(pipe(pipefd) < 0) { //創建管道
- perror("pipe:");
- exit(1);
- }
- if((pid = fork()) == 0) { //子進程從管道讀取
- close(pipefd[1]);
- sleep(2);
- if((recvBytes = read(pipefd[0], recvBuf, MAX)) == -1) {
- perror("read");
- exit(1);
- }
- printf("Receive %d Bytes data:%s\n", recvBytes, recvBuf);
- close(pipefd[1]);
- }
- else if(pid > 0) { //父進程向管道寫入
- close(pipefd[0]);
- strcpy(sendBuf, "Pipe test!");
- if((sendBytes = write(pipefd[1], sendBuf, MAX)) == -1) {
- perror("write");
- exit(1);
- }
- printf("Send %d Bytes data:%s\n", sendBytes, sendBuf);
- close(pipefd[0]);
- sleep(5);
- }
- exit(0);
- }
運行結果:
- jimmy@MyPet:~/code/ipc$ gcc -o -ggdb3 ipc ipc1.c
- jimmy@MyPet:~/code/ipc$ ./ipc
- Send 4096 Bytes data:Pipe test!
- Receive 4096 Bytes data:Pipe test!
非正常測試結果及總結:
1)註釋掉子進程中的接收部分,測試結果爲父進程依然能向管道發送數據。
2)註釋掉父進程中的發送部分,測試結果爲子進程依然能從管道接收數據,recvBytes = 0。
3)子進程改爲關閉pipefd[0]從pipefd[1]接收,父進程改爲關閉pipefd[1]從pipefd[0]發送,測試結果爲發送和接收均失敗,提示“BAD FILE DESCRIPTOR”。說明了在創建管道時管道對於讀寫接口已經有了嚴格的定義,pipefd[0]爲讀端,pipefd[1]爲寫端,不能隨意更改。讀端寫,寫端讀都會產生錯誤。
4)在父進程寫入之前關閉子進程讀端,在gdb調試情況下父進程寫入時產生“Program received signal SIGPIPE, Broken pipe.”,管道破裂。
5)無名管道只能用於具有親緣關係的父子進程間通信。
6)管道是半雙工的,因此要達到全雙工通信創建管道是必須創建兩條。
2-1 有名管道簡介
也稱FIFO,系統提供一個路徑名與此管道關聯,以FIFO形式存在與文件系統中。
生存週期從被創建開始,到該管道文件被刪除(進程結束不會造成管道消失)。
數據在管道之間以無格式流式傳遞。
只需要建立一個有名管道便可進行讀寫操作。遵從FIFO原則,不保證操作的原子性。
優點是使用方便簡單,可以作爲任何進程間通信手段,缺點是功能上有很多限制。
2-2 相關函數
創建FIFO:
- #include <sys/types.h>
- #include <sys/stat.h>
- int mkfifo( const char * pathname, mode_t mode );
成功後返回0;出錯返回-1。參數2與打開文件的mode參數一致。
需要注意的是與一般文件不同,當以讀方式打開FIFO時,如果設置了阻塞且無進程寫打開此FIFO,將會阻塞。當以寫方式打開FIFO時,如果設置了阻塞切無進程讀打開此FIFO,將會阻塞。
讀寫FIFO:
使用文件I/O的write和read函數,參考http://blog.csdn.net/jmy5945hh/article/details/7465879
關閉FIFO:
使用close函數,參考http://blog.csdn.net/jmy5945hh/article/details/7465879
close函數並不刪除FIFO,而只是關閉。unlink函數接觸關聯關係,也不刪除FIFO。
2-3 有名管道測試代碼
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <string.h>
- #include <sys/stat.h>
- #define FIFO_SERV "/home/jimmy/code/ipc/fifo_serv" //管道名,是一個文件
- #define MAX 4096
- int main(int argc, char *argv[]) {
- int pipewrfd, piperdfd, sendBytes, recvBytes;
- unsigned char sendBuf[MAX], recvBuf[MAX];
- pid_t pid;
- memset(sendBuf, 0x00, MAX);
- memset(recvBuf, 0x00, MAX);
- if((pid = fork()) == 0) { //子進程
- if((piperdfd = open(FIFO_SERV,O_RDONLY,0)) == -1) { //只讀方式打開有名管道
- perror("child open FIFO_SERV");
- exit(1);
- }
- recvBytes = read(piperdfd, recvBuf, MAX); //從管道讀出
- if(recvBytes == -1 && errno == EAGAIN)
- printf("no data avlaible\n");
- printf("recv %d Bytes data:%s\n", recvBytes, recvBuf);
- pause();
- unlink(FIFO_SERV);
- }
- else if(pid > 0) {
- if((mkfifo(FIFO_SERV, O_CREAT|O_EXCL|0777) < 0) && (errno != EEXIST)) //創建有名管道
- printf("cannot create fifoserver\n");
- if((pipewrfd = open(FIFO_SERV, O_WRONLY,0)) == -1) { //只寫方式打開有名管道
- perror("father open FIFO_SERV");
- exit(1);
- }
- strcpy(sendBuf, "Name pipe test!");
- sendBytes = write(pipewrfd, sendBuf, MAX); //向管道寫入
- if(sendBytes == -1 && errno == EAGAIN)
- printf("write to fifo error; try later\n");
- }
- }
運行結果:
- jimmy@MyPet:~/code/ipc$ gcc -g -o fifo fifo.c
- jimmy@MyPet:~/code/ipc$ ./fifo
- recv 4096 Bytes data:Name pipe test!
非正常測試結果及總結:
1)管道名錯誤,無法創建有名管道
2)註釋掉子進程中的接收部分,測試結果爲父進程依然能向管道發送數據。
3)註釋掉父進程中的發送部分,測試結果爲子進程依然能從管道接收數據,recvBytes = 0。
4)有名管道可以看作無名管道的升級版,克服了只能在親緣關係進程間使用的門檻。
5)由於需要通過文件操作,在通信速度上不具有優勢
(完)