• Linux多进程(二)进程通信方式一 管道


    管道的是进程间通信(IPC - InterProcess Communication)的一种方式,管道的本质其实就是内核中的一块内存(或者叫内核缓冲区),这块缓冲区中的数据存储在一个环形队列中,因为管道在内核里边,因此我们不能直接对其进行任何操作。

    因为管道数据是通过队列来维护的,我们先来分析一个管道中数据的特点:

    • 管道对应的内核缓冲区大小是固定的,默认为4k(也就是队列最大能存储4k数据)
    • 管道分为两部分:读端和写端(队列的两端),数据从写端进入管道,从读端流出管道
    • 管道中的数据只能读一次,做一次读操作之后数据也就没有了(读数据相当于出队列)
    • 管道是单工的:数据只能单向流动, 数据从写端流向读端
    • 对管道的操作(读、写)默认是阻塞的

    一、匿名管道

    匿名管道是管道的一种,既然是匿名也就是说这个管道没有名字,但其本质是不变的,就是位于内核中的一块内存,匿名管道拥有上面介绍的管道的所有特性。

    pipe 函数用于创建一个管道,以实现进程间通信。

    #include 
    
    int pipe(int fd[2]);
    
    • 1
    • 2
    • 3

    管道容量的大小默认是65536字节。我们可以使用fcntl函数来修改管道容量。

    下面举一个在多进程程序中管道通信的例子:

    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        int pipefd[2];
        char buf[1024];
    
        // 创建管道
        if (pipe(pipefd) == -1)
        {
            perror("pipe error! \n");
            return 1;
        }
    
        for (int i = 0; i < 8; i++)
        {
            // 创建子进程
            pid_t pid = fork();
    
            if (pid == -1)
            {
                perror("fork error!\n");
                return 1;
            }
            else if (pid == 0)
            {
                // 子进程关闭写端
                close(pipefd[1]);
                // 从管道读取数据
                memset(buf, 0, 1024);
                read(pipefd[0], buf, sizeof(buf));
                // 打印数据
                printf("pid = %d :Child process received: %s\n", getpid(), buf);
                // 子进程关闭读取端
                close(pipefd[0]);
                // 子进程直接break,以免创建更多的子进程
                break;
            }
        }
    
        if (getpid() != 0)
        {
            // 父进程关闭读取端
            close(pipefd[0]);
            // 向管道写入数据
            const char *message = "Hello from parent process";
            for (int i = 0; i < 8; i++)
            {
                write(pipefd[1], message, strlen(message));
                sleep(1);
            }
            // 在所有数据都写入后再关闭写入端
            close(pipefd[1]);
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    生成了八个子进程,八个子进程阻塞在read处,等待父进程的消息,父进程发了八次消息,每次间隔1秒。看一下仿真
    请添加图片描述

    二、有名管道

    有名管道拥有管道的所有特性,之所以称之为有名是因为管道在磁盘上有实体文件, 文件类型为p ,有名管道文件大小永远为0,因为有名管道也是将数据存储到内存的缓冲区中,打开这个磁盘上的管道文件就可以得到操作有名管道的文件描述符,通过文件描述符读写管道存储在内核中的数据。

    有名管道也可以称为 fifo (first in first out),使用有名管道既可以进行有血缘关系的进程间通信,也可以进行没有血缘关系的进程间通信。创建有名管道的方式有两种,一种是通过命令,一种是通过函数。

    2.1、创建有名管道

    通过命令

    mkfifo 有名管道的名字
    
    • 1

    通过函数

    #include 
    #include 
    
    int mkfifo(const char *pathname, mode_t mode);
    
    • 1
    • 2
    • 3
    • 4
    • pathname: 要创建的有名管道的名字
    • mode: 文件的操作权限, 和open()的第三个参数一个作用,最终权限: (mode & ~umask)
    • 返回值:创建成功返回 0,失败返回 -1

    2.2、进程间通信

    不管是有血缘关系还是没有血缘关系,使用有名管道实现进程间通信的方式是相同的,就是在两个进程中分别以读、写的方式打开磁盘上的管道文件,得到用于读管道、写管道的文件描述符,就可以调用对应的read()、write()函数进行读写操作了。

    有名管道操作需要通过 open() 操作得到读写管道的文件描述符,如果只是读端打开了或者只是写端打开了,进程会阻塞在这里不会向下执行,直到在另一个进程中将管道的对端打开。

    写管道的进程

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        // 1. 创建有名管道文件
        int ret = mkfifo("./testfifo", 0664);
        if(ret == -1)
        {
            perror("mkfifo");
            exit(0);
        }
        printf("管道文件创建成功...\n");
    
        // 2. 打开管道文件
        int wfd = open("./testfifo", O_WRONLY);
        if(wfd == -1)
        {
            perror("open");
            exit(0);
        }
        printf("以只写的方式打开文件成功...\n");
    
        // 3. 循环写管道
        int i = 0;
        while(i<100)
        {
            char buf[1024];
            sprintf(buf, "hello, fifo, 我在写管道...%d\n", i);
            write(wfd, buf, strlen(buf));
            i++;
            sleep(1);
        }
        close(wfd);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    读管道的进程

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        // 1. 打开管道文件
        int rfd = open("./testfifo", O_RDONLY);
        if(rfd == -1)
        {
            perror("open");
            exit(0);
        }
        printf("以只读的方式打开文件成功...\n");
    
        // 2. 循环读管道
        while(1)
        {
            char buf[1024];
            memset(buf, 0, sizeof(buf));
            // 读是阻塞的, 如果管道中没有数据, read自动阻塞
            int len = read(rfd, buf, sizeof(buf));
            printf("读出的数据: %s\n", buf);
            if(len == 0)
            {
                // 写端关闭了, read解除阻塞返回0
                printf("管道的写端已经关闭, 拜拜...\n");
                break;
            }
    
        }
        close(rfd);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    仿真结果

    请添加图片描述

  • 相关阅读:
    小程序拼团业务,并发支付导致重复给用户返利bug
    uniapp的async、await用法介绍
    Linux内核基础 - list_move_tail函数详解
    怎么更改文件创建日期?
    使用IO流完成项目实战水果库存系统
    python基础
    交通物流模型 | 基于自监督学习的交通流预测模型
    [附源码]Python计算机毕业设计Django咖啡销售平台
    我作为前端开发者对低代码平台的理解和搭建流程(一)
    16_开发工具IntelliJ IDEA
  • 原文地址:https://blog.csdn.net/weixin_43903639/article/details/138155634