1. 程式人生 > >程序間通訊管道

程序間通訊管道

一、常用的方式

(1) 管道

(2) System V

(3) POSIX

二、目的

(1) 資料傳輸

(2) 資源共享

(3) 通知事件

(4) 程序控制

三、 本質

讓兩個不同的程序看到一份公共的資源

四、匿名管道

1. 簡單用法: 

2.  建立管道

Makefile:

main:main.c
    gcc main.c -o main 

.PHONY:clean
clean:
    rm -f main

程式碼:

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if (ret < 0)
    {
        perror("pipe");
        return 1;
    }

    pid_t id = fork();
    if (id == 0)
    {
        close(fd[0]);
        const char *msg = "hello child\n";
        while (1)
        {
            sleep(1);
            write(fd[1], msg, strlen(msg));
        }
    }
    else 
    {
        close(fd[1]);
        char buf[64];
        while (1)
        {
            ssize_t s = read(fd[0], buf, sizeof(buf) - 1);
            if (s > 0)
            {
                buf[s] = '\0';
                printf("parent saying:%s\n", buf);
            }
        }
    }

    return 0;
}

3. 深度理解管道 

(1) 父程序建立管道

(2)父程序 fork 子程序

(3)父程序關閉 fd[0](讀), 子程序關閉 fd[1](寫)

4. 特性

(1) 單向傳遞

(2) 具有血緣關係

(3) 自帶同步互斥

(4) 生命週期隨程序(程序退出,管道釋放)

(5) 提供面向位元組流的服務

5. 概念

(1) 資料不一致:讀寫雙方因為訪問共同資源二導致的資料不一致的問題

例如: hello word    和    shagua

讀第一個讀到空格時,讀到第二個,就出現了錯誤

(2) 臨界資源:把兩個進現在程看到的看到的公共資源稱為臨界資源

(3) 臨界區:每個程序中訪問臨界資源的那段程式碼稱為臨界區

(4) 互斥:在任何一個時間點,臨界區在訪問臨界資源時,有且僅有一個人在訪問,即你用的時候別人都不能用,別人用的時候,你也不能去用

     同步:在現有的互斥基礎上,讓多個人訪問臨界資源具有了順序性,即我們大家利用一些共同的資源區,大家一起合作,完成某些事情,但是我在幹某些小事的時候,可能要等到你做完另一些小事

(5) 原子性:在進行某些資源的操作時時,不會有中間狀態

6. 四種情況

(1) 寫方一直在寫,讀端不讀,但讀端不關閉檔案描述符,寫方因為互斥與同步在把管道寫滿之後就會停下來

程式碼檢驗:

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if (ret < 0)
    {
        perror("pipe");
        return 1;
    }

    pid_t id = fork();
    if (id == 0)
    {
        close(fd[0]);
        int count = 0;
        const char *msg = "hello child\n";
        while (1)
        {
            write(fd[1], msg, strlen(msg));
            printf("%d\n", count++);
        }
    }
    else 
    {
        close(fd[1]);
        char buf[64];
        while (1)
        {
            sleep(5);
            ssize_t s = read(fd[0], buf, sizeof(buf) - 1);
            if (s > 0)
            {
                buf[s] = '\0';
                printf("parent saying:%s\n", buf);
            }
        }
    }

    return 0;
}

(2) 如果讀寫雙方不掛不關閉檔案描述符,一個不寫,一個不讀,雙方都要等待,這個過程就叫同步

程式碼檢驗:

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if (ret < 0)
    {
        perror("pipe");
        return 1;
    }

    pid_t id = fork();
    if (id == 0)
    {
        close(fd[0]);
        const char *msg = "hello child\n";
        while (1)
        {
            write(fd[1], msg, strlen(msg));  
            sleep(10);
        }
    }
    else 
    {
        close(fd[1]);
        char buf[64];
        int count = 0;
        while (1)
        {
            sleep(5);
            ssize_t s = read(fd[0], buf, sizeof(buf) - 1);
            if (s > 0)
            {
                buf[s] = '\0';
                printf("parent saying:%s\n", buf);
            }
            printf("parent read:%d\n", count);
        }
    }

    return 0;
}

(3) 如果寫端一直在寫,有朝一日寫端不寫,並把寫端關閉,讀端會一直讀,直到把管道中檔案描述符讀完,最後讀返回值 0,這個 0 代表著讀到了檔案的結尾

程式碼檢驗:

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if (ret < 0)
    {
        perror("pipe");
        return 1;
    }

    pid_t id = fork();
    if (id == 0)
    {
        int count = 0;
        close(fd[0]);
        const char *msg = "hello child\n";
        while (1)
        {
            write(fd[1], msg, strlen(msg));  
            printf("%d\n", count++);
            if (count > 0)
            {
                close(fd[1]);
                break;
            }
             sleep(1);
        }
    }
    else 
    {
        close(fd[1]);
        char buf[64];
        while (1)
        {
            ssize_t s = read(fd[0], buf, sizeof(buf) - 1);
            if (s > 0)
            {
                buf[s] = '\0';
                printf("parent saying:%s\n", buf);
            }
            else if (s == 0)
            {
                printf("pipe done, break;\n");
                break;
            }
        }
    }

    return 0;
}

(4)  寫端一直在寫,讀端不讀且關閉檔案描述符,寫端會因為操作異常被作業系統向目標傳送13號訊號終止

程式碼檢驗:

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if (ret < 0)
    {
        perror("pipe");
        return 1;
    }

    pid_t id = fork();
    if (id == 0)
    {
        int count = 0;
        close(fd[0]);
        const char *msg = "hello child\n";
        while (1)
        {
            write(fd[1], msg, strlen(msg));  
            printf("%d\n", count++);
            sleep(1);
        }
    }
    else 
    {
        close(fd[1]);
        char buf[64];
        while (1)
        {
            ssize_t s = read(fd[0], buf, sizeof(buf) - 1);
            if (s > 0)
            {
                buf[s] = '\0';
                printf("parent saying:%s\n", buf);
                sleep(3);
                break;
            }  
        }
        close(fd[0]);

        int status = 0;
        wait(&status);
        printf("sig:%d\n", status&0x7F);
    }
    return 0;
}

7. 管道大小

管道大小一般為4096

五、命名管道

1. 建立命名管道

mkfifo filename

2. 程式碼實現管道

reader.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main()
{
    int fd = open("./myfifo", O_RDONLY);
    if (fd < 0)
    {
        perror("open");
        return 1;
    }
    printf("open ok!\n");
    while (1)
    {
        char buf[1024] = {0};
        ssize_t read_size = read(fd, buf, sizeof(buf) - 1);
        if (read_size < 0)
        {
            perror("read");
            return 1;
        }
        if (read_size == 0)
        {
            perror("read done");
            return 0;
        }
        buf[read_size] = '\0';
        printf("%s\n", buf);
    }
    close(fd);
    return 0;
}

writer.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main()
{
    int fd = open("./myfifo", O_WRONLY);
    if (fd < 0)
    {
        perror("open");
        return 1;
    }
    while (1)
    {
        printf("> ");
        fflush(stdout);
        char buf[1024] = {0};
        ssize_t read_size = read(0, buf, sizeof(buf) - 1);
        if (read_size < 0)
        {
            perror("read");
            return 1;
        }
        if (read_size == 0)
        {
            printf("read done!\n");
            return 0;
        }
        buf[read_size] = '\0';
        write(fd, buf, strlen(buf));
    }
    close(fd);
    return 0;
}

執行截圖: