1. 程式人生 > 實用技巧 >IPC--程序間通訊

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