IPC--程序間通訊
常用的程序間通訊方式:
a.管道(使用最簡單)匿名管道
b.訊號(開銷最小)
c.共享對映區(無血緣關係)
d.本地套接字(最穩定)
e.FIFO(命名管道)
1.管道:
pipe:管道一般讀寫行為
2.fifo:(有名管道)
用於非血緣關係程序間通訊
命令:mkfifo
函式:mkfifo
3.共享對映區:
mmap
函式的引數使用注意事項
用於非血緣關係程序間通訊
管道的概念:
管道是一種最基本的IPC機制,作用於有血緣關係的程序之間,完成資料傳遞。呼叫pipe系統函式即可建立一個管道。有如下特質:
1.其本質是一個偽檔案(實為核心緩衝區)
七種檔案型別:
1). - 檔案 2). d 目錄 3). l 字元連結 此三種為真實檔案,佔據磁碟儲存空間
4). s 套接字 5). b 塊裝置 6). c 字元裝置 7). p 管道 此四種為偽檔案
2.有兩個檔案描述符引用,一個表示讀端,一個表示寫端。
3.規定資料從管道的寫端流入管道,從讀端流出。
管道的原理:管道實為核心使用環形佇列機制,藉助核心緩衝區(4k)實現。【使用ulimit -a檢視,函式fpathconf 參2:_PC_PIPE_BUF】
管道的侷限性:
1.資料自己讀不能自己寫
2.資料一旦被讀走,便不在管道中存在,不可反覆讀取。
3.由於管道採用半雙工通訊方式。因此資料只能在一個方向上流動
4.只能在有公共祖先的程序間使用管道
常見的通訊方式有:單工通訊、半雙工通訊、全雙工通訊。
模擬一個管道間pipe的通訊:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main(void) { int fd[2]; pid_t pid; int ret = pipe(fd); if(ret == -1){ perror("pipe error."); exit(1); } pid = fork(); if(pid == -1){ perror("fork error."); exit(1); } else if(pid == 0) { //規定子程序負責讀端,父程序負責寫端 //預設fd[0]--讀端,fd[1]--寫端 //關閉子程序的寫端 close(fd[1]); char buf[1024]; ret= read(fd[0], buf, sizeof(buf)); if(ret == 0){ printf("---finished read---"); } write(STDOUT_FILENO, buf, ret); } else { //關閉父程序的讀端 close(fd[0]); write(fd[1], "hello pipe\n", strlen("hello pipe\n")); } }
共享記憶體:
1.mmap函式:引數、返回值
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
返回:
成功:返回建立的對映區首地址
失敗:MAP_FAILED 巨集
引數:
addr: 建立對映區的首地址,由Linux核心指定。使用時,直接傳遞NULL
length: 預建立對映區的大小
prot: 對映區許可權PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags: 標誌位引數(常用於設定更新物理區域、設定共享、建立匿名對映區)
MAP_SHARED: 會將對映區所做的操作反映到物理裝置(磁碟)上
MAP_PRIVATE: 對映區所做的修改不會反映到物理裝置
fd: 用來建立對映區的檔案描述符
offset: 對映檔案的偏移(4k的整數倍)
mmap示例:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/mman.h> int main(void) { int len, ret; char *p = NULL; int fd = open("mytest.txt", O_CREAT|O_RDWR, 0644); if(fd < 0) { perror("open error:"); exit(1); } len = ftruncate(fd, 5); if(len == -1) { perror("ftruncate error:"); exit(1); } p = mmap(NULL, 5, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if(p == MAP_FAILED) { perror("mmap error:"); exit(1); } strcpy(p, "abc\n"); ret = munmap(p, 5); if(ret == -1) { perror("munmap error:"); exit(1); } close(fd); return 0; }
munmap函式
同malloc函式申請記憶體空間類似的,mmap建立的對映區在使用之後也應呼叫類似free的函式來釋放。
使用mmap時的注意事項:
1).建立對映區的過程中,隱含著一次對對映檔案的讀操作
2).當MAP_SHARED時,要求:對映區的許可權應<=檔案開啟的許可權(出於對對映區的保護)。而MAP_PRIVATE則無所謂,
因為mmap中的許可權是對記憶體的限制。
3).對映區的釋放與檔案關閉無關。只要對映建立成功,檔案可以立即關閉。
4).特別注意,當對映檔案大小為0時,不能建立對映區。所以:用於對映的檔案必須要有實際大小!
mmap使用時常常會出現匯流排錯誤,通常是由於共享檔案儲存空間大小引起的。
5).munmap傳入的地址一定是mmap的返回地址,堅決杜絕指標++操作。
6).如果有檔案偏移量必須為4k的整數倍(4*1024*n)
7).mmap建立對映區的出錯概率非常高,一定要檢查返回值,確保對映區建立成功再進行後續操作。
2.藉助共享記憶體放磁碟檔案,藉助指標訪問磁碟檔案
3.父子程序、血緣關係程序通訊
4.匿名對映區
通過使用我們發現,使用對映區來完成檔案讀寫操作十分方便,父子程序間通訊也比較容易。但缺陷是,每次建立對映區
一定要依賴一個檔案才能實現。通常為了建立對映區要open一個temp檔案,建立好了再unlink、close掉,比較麻煩。可以直接
使用匿名對映來代替。其實Linux系統給我們提供了建立匿名對映區的方法,無需依賴一個檔案即可建立對映區。同樣需要藉助
標誌位引數flags來指定。
使用MAP_ANONYMOUS (或 MAP_ANON),如:
int *p=mmap(NULL,4,PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
"4"可以為任意值,可以根據實際需要填寫。
需注意的是,MAP_ANONYMOUS和MAP_ANON這兩個巨集是Linux作業系統特有的巨集。在類Unix系統中無該巨集定義,可使用下面兩步來建立匿名對映區:
1). fd=open("/dev/zero", O_RDWR);
2). P=mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
5.mmap無血緣關係程序間通訊
mmap_r.c和mmap_w.c共用一個檔案file_shared模擬無血緣關係程序間通訊
mmap_r.c表示讀程序
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> struct STU { int id; char name[20]; char sex; }; void sys_err(char *str){ perror(str); exit(1); } int main(int argc, char *argv[]){ int fd; struct STU student; struct STU *mm; if(argc < 2) { printf("./a.out file_shared\n"); exit(-1); } fd = open(argv[1], O_RDONLY); if(fd == -1){ sys_err("open error:"); } mm = mmap(NULL, sizeof(student), PROT_READ, MAP_SHARED, fd, 0); if(mm == MAP_FAILED){ sys_err("mmap error:"); } close(fd); while(1) { printf("id=%d\tname=%s\t%c\n", mm->id, mm->name, mm->sex); sleep(2); } munmap(mm, sizeof(student)); return 0; }
mmap_w.c表示寫程序
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> struct STU { int id; char name[20]; char sex; }; void sys_err(char *str){ perror(str); exit(1); } int main(int argc, char *argv[]){ int fd; struct STU student = {10, "xiaoming", 'm'}; char *mm; if(argc < 2) { printf("./a.out file_shared\n"); exit(-1); } fd = open(argv[1], O_RDWR | O_CREAT, 0664); ftruncate(fd, sizeof(student)); mm = mmap(NULL, sizeof(student), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(mm == MAP_FAILED){ sys_err("mmap error:"); } close(fd); while(1) { memcpy(mm, &student, sizeof(student)); student.id++; sleep(1); } munmap(mm, sizeof(student)); return 0; }
先執行mmap_w.c後執行mmap_r.c
可以使用 strace mmap_w.out 來追蹤mmap_w.out執行過程中呼叫了哪些系統函式。
yum install strace -y 來安裝strace
模擬程序組:cat | cat | cat | cat
使用 ps ajx來檢視
殺掉程序組 kill -9 -gpid