进程间通信管道

一、常用的方式

(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;
}

运行截图:

猜你喜欢

转载自blog.csdn.net/xinwenhuayu/article/details/84201835