進程間通訊(IPC, InterProcess Communication)是指在不一樣進程之間傳播或交換信息。
主要的方式有管道(包括無名管道,高級管道和命名管道),消息隊列, 信號量, 共享內存, Socket等。 其中Socket能夠用於不一樣主機上的進程間通訊。
進程通訊的主要目的以下:html
數據傳輸:一個進程須要將它的數據發送給另外一個進程,發送的數據量在一個字節到幾M字節之間linux
共享數據:多個進程想要操做共享數據,一個進程對共享數據的修改,別的進程應該馬上看到。socket
通知事件:一個進程須要向另外一個或一組進程發送消息,通知它(它們)發生了某種事件(如進程終止時要通知父進程)。函數
資源共享:多個進程之間共享一樣的資源。爲了做到這一點,須要內核提供鎖和同步機制。ui
進程控制:有些進程但願徹底控制另外一個進程的執行(如Debug進程),此時控制進程但願可以攔截另外一個進程的全部陷入和異常,並可以及時知道它的狀態改變。線程
匿名管道( pipe ):管道是一種半雙工的通訊方式,數據只能單向流動,並且只能在具備親緣關係的進程間使用。進程的親緣關係一般是指父子進程關係。設計
高級管道(popen):將另外一個程序當作一個新的進程在當前程序進程中啓動,則它算是當前程序的子進程,這種方式咱們成爲高級管道方式。調試
命名管道 (named pipe) : 有名管道也是半雙工的通訊方式,可是它容許無親緣關係進程間的通訊。rest
消息隊列( message queue ) : 消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩衝區大小受限等缺點。code
信號量( semophore ) : 信號量是一個計數器,能夠用來控制多個進程對共享資源的訪問。它常做爲一種鎖機制,防止某進程正在訪問共享資源時,其餘進程也訪問該資源。所以,主要做爲進程間以及同一進程內不一樣線程之間的同步手段。
信號 ( sinal ) : 信號是一種比較複雜的通訊方式,用於通知接收進程某個事件已經發生。
共享內存( shared memory ) :共享內存就是映射一段能被其餘進程所訪問的內存,這段共享內存由一個進程建立,但多個進程均可以訪問。共享內存是最快的 IPC 方式,它是針對其餘進程間通訊方式運行效率低而專門設計的。它每每與其餘通訊機制,如信號兩,配合使用,來實現進程間的同步和通訊。
套接字( socket ) : 套解口也是一種進程間通訊機制,與其餘通訊機制不一樣的是,它可用於不一樣機器間的進程通訊。
本文主要講解命令管道。
命名管道是在文件系統中做爲一個特殊的設備文件存在的, 不一樣祖先的進程之間能夠經過管道共享數據,當全部訪問管道的進程執行結束後,命名管道將繼續存在以便之後使用。
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode);
mkfifo用於建立一個FIFO文件, 參數mode決定文件權限。
The file creation mask of the process (see umask()) modifies the file permission bits of mode. The owner ID of the FIFO is set to the effective user ID of the process. The FIFO group ID is set to the effective group ID of the process.
若是成功,那麼mkfifo()返回0;若是失敗,那麼返回-1, 而且errno被賦爲下面的值:
EACCES: 某級路徑無訪問權限,或pathname指向的目錄無寫權限
EEXIST: 同名文件已存在
EINVAL: 在NuTCRCKER平臺上描述路徑不在fifos文錄下
ENAMETOOLONG: pathname名太長
ENOENT: 路徑中某一級目錄不存在
ENOSPC: 磁盤滿
ENOTDIR: 路徑中某一級不是目錄
EROFS:文件系統只讀
詳細信息能夠查看: [這裏](http://man7.org/linux/man-pag...)
file mode以下:
S_IRWXU: 00700 read, write, execute/search by owner
S_IRUSR: 00400 read permission, owner
S_IWUSR: 00200 write permission, owner
S_IXUSR: 00100 execute/search permission, owner
S_IRWXG: 00070 read, write, execute/search by group
S_IRGRP: 00040 read permission, group
S_IWGRP: 00020 write permission, group
S_IXGRP: 00010 execute/search permission, group
S_IRWXO: 00007 read, write, execute/search by others
S_IROTH: 00004 read permission, others
S_IWOTH: 00002 write permission, others
S_IXOTH: 00001 execute/search permission, others
S_ISUID: 0004000 set-user-ID on execution
S_ISGID: 0002000 set-group-ID on execution
S_ISVTX: 0001000 on directories, restricted deletion flag
系統I/O函數如open, close, read, write均可以操做FIFO文件
默認不指定O_NONBLOCK時即爲阻塞模式
open以只讀方式打開FIFO時, 要阻塞到某個進程爲寫而打開此FIFO
open以只寫方式打開FIFO時,要阻塞到某個進程爲讀而打開此FIFO
open以只讀+只寫方式打開FIFO時,調用read函數時read會阻塞
調用write函數向FIFO寫數據時,當緩衝區已滿時write會阻塞
通訊過程當中若寫進程先退出了,則調用read函數從FIFO裏讀數據時不阻塞;若寫進程又從新運行,則調用read函數從FIFO裏讀數據時又恢復阻塞
通訊過程當中若讀進程先退出, 則寫進程向FIFO寫數據,會收到SIGPIPE信號而退出
open以讀寫方式打開FIFO時,open不阻塞
open以只讀方式打開FIFO,若是沒有進程爲寫而打開FIFO, 只讀open成功,而且open不阻塞
open以只寫方式打開FIFO, 若是沒有進程爲讀而打開FIFO,只寫open將出錯返回-1 (ENXIO: No Such device or address)
read, write讀寫FIFO中數據時不阻塞
通訊過程當中, 讀進程退出後, 寫進程向命名管道內寫數據時,寫進程會收到SIGPIPE信號而退出
reader
// // Created by : Harris Zhu // Filename : fifo_read.cpp // Author : Harris Zhu // Created On : 2017-08-17 16:46 // Last Modified : // Update Count : 2017-08-17 16:46 // Tags : // Description : // Conclusion : // //======================================================================= #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define FIFO_SERVER "myfifo" #define OPEN_MODE O_RDONLY | O_NONBLOCK #define FIFO_MODE O_CREAT|O_RDWR|O_NONBLOCK int main(int argc, char** argv) { char buf_r[100]; int fd; int nread; int res; if (((res=mkfifo(FIFO_SERVER, FIFO_MODE)) < 0) && (errno != EEXIST)) { printf("can not creat fifoserver %d :\n", res, errno); exit(1); } printf("preparing for reading bytes...\n"); char cmd[100]; sprintf(cmd, "chmod 704 %s", FIFO_SERVER); system(cmd); fd = open(FIFO_SERVER, OPEN_MODE); if (fd == -1) { perror("error in openning fifo server"); exit(1); } int i=0; int len; while (i++<21) { memset(buf_r, 0, sizeof(buf_r)); if ((nread = read(fd, buf_r, sizeof(buf_r))) < 0) { if (errno == EAGAIN) { printf("no data yet\n"); sleep(1); } } else { if(nread > 0) { printf("read %s from FIFO %d \n", buf_r, i); } sleep(1); } } // pause(); close(fd); unlink(FIFO_SERVER); return 0; }
writer
// // Created by : Harris Zhu // Filename : fifo_write.c // Author : Harris Zhu // Created On : 2017-08-17 18:04 // Last Modified : // Update Count : 2017-08-17 18:04 // Tags : // Description : // Conclusion : // //======================================================================= #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <time.h> #define FIFO_SERVER "myfifo" #define FIFO_MODE O_CREAT|O_NONBLOCK|O_RDWR #define FILE_MODE O_WRONLY | O_NONBLOCK int main(int argc, char** argv) { int fd; char w_buf[100]; char w_t_buf[50]; const char *hstr = "hello world"; if(mkfifo(FIFO_SERVER, FIFO_MODE<0)&& (errno != EEXIST)) { perror("failed to create fifo server"); exit(1); } char cmd[100]; sprintf(cmd, "chmod 704 %s", FIFO_SERVER); system(cmd); int nwrite; fd = open(FIFO_SERVER, FILE_MODE); if (fd == -1) { if (errno == ENXIO) { printf("open errop;no reading process\n"); } else { perror("open error"); exit(EXIT_FAILURE); } } if (argc >= 2) { strcpy(w_t_buf, argv[1]); } else { strcpy(w_t_buf, hstr); } int i=0; int n; time_t tp; while(i++<20) { time(&tp); n=sprintf(w_buf, "Process %d is sending %s at %s", getpid(), w_t_buf, ctime(&tp)); if ((nwrite = write(fd, w_buf, n)) < 0) { if (errno == EAGAIN) { printf("the fifo has not been read yet.Please try later\n"); } else { exit(1); } } printf("Send Message to FIFO: %s \n", w_buf); sleep(1); } close(fd); return 0; }
先運行reader, 而後運行writer
Makefile內容以下
build: gcc -g read.c -o read gcc -g write.c -o write run: xterm -e ./read & sleep 1 xterm -e ./write & clean: rm -rf read write
由於reader先開讀,當writer開始的時候,reader已經在while裏走過和個循環了,因此reader會先結束,這時writer還想向FIFO裏寫數據,就會遇到「Broken pipe」問題
若是把writer的等待時間改小, 那麼收端比較慢,因此fifo裏的數據會比較多,reader收到的數據就不是一行一行
harriszh Fri 17:30@ ~/trunk/cpp/fifo1$ ./fifo_read preparing for reading bytes... read Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello worl from FIFO read d at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Proce from FIFO read ss 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at from FIFO read Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11 from FIFO read 863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri A from FIFO read ug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 i from FIFO read s sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 from FIFO read 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sen from FIFO read ding hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:3 from FIFO read 1:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending from FIFO read hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 from FIFO read 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello from FIFO read world at Fri Aug 18 17:31:57 2017 Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017 from FIFO
看完非阻塞再看阻塞就簡單點了
代碼以下
Reader
// // Created by : Harris Zhu // Filename : write.c // Author : Harris Zhu // Created On : 2017-08-17 22:05 // Last Modified : // Update Count : 2017-08-17 22:05 // Tags : // Description : // Conclusion : // //======================================================================= #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<signal.h> #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(int argc, char *argv[]) { int outfd = open("Makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (outfd == -1) ERR_EXIT("open Makefile2 error"); int infd; infd = open("tp", O_RDONLY ); if (infd == -1) ERR_EXIT("open tp error"); char buf[1024]; int n; while ((n = read(infd, buf, 1024)) > 0) write(outfd, buf, n); close(infd); close(outfd); unlink("tp"); // delete a name and possibly the file it refers to return 0; }
Writer
// // Created by : Harris Zhu // Filename : read.c // Author : Harris Zhu // Created On : 2017-08-17 22:05 // Last Modified : // Update Count : 2017-08-17 22:05 // Tags : // Description : // Conclusion : // //======================================================================= #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<signal.h> #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(int argc, char *argv[]) { mkfifo("tp", 0644); int infd = open("Makefile", O_RDONLY ); if (infd == -1) ERR_EXIT("open Makefile error"); int outfd; outfd = open("tp", O_WRONLY ); if (outfd == -1) ERR_EXIT("open tp error"); char buf[1024]; int n; while ((n = read(infd, buf, 1024)) > 0) write(outfd, buf, n); printf("Makefile2 is created successfully!\n") close(infd); close(outfd); return 0; }
先運行writer, 再運行reader, 緣由是FIFO由writer建立
Makefile
build: gcc -g read.c -o read gcc -g write.c -o write clean: rm -rf Makefile2 rm -rf ./read ./write run: xterm -e ./write & sleep 0.1 xterm -e ./read & .PHONY: clean
運行後,reader端會把從writer寫入的內容保存到Makefile2裏
非阻塞模式的理解有點繞,在編寫程序時也要注意二者的通信機制以及關閉條件。
能夠本身先寫個例子,看看能不能正確工做。通常第一個例子是很是難以正確工做的,須要在調試的過程當中,來加深瞭解。
若是有任何疑問,歡迎發郵件給我。