Linux:匿名管道(进程间通信二)

今天我们来讲讲管道之一的匿名管道,话不多说,我们正式开始啦

1.什么是管道

管道是Unix中最古⽼的进程间通信的形式,我们把从⼀个进程连接到另⼀个进程的⼀个数据流称为⼀个"管道"

在最开始的时候,其实顶尖大佬并不打算创建一个新的东西来进行管道通信,而是打算采用现有的东西来解决进程间通信的问题,于是他们就想到了文件,对于父子进程来说,父子进程有同样的struct file,来指向同一个被打开的文件,而对于一个文件来说,他有自己的缓冲区,是不是可以让父进程将内容写入文件缓冲区,让子进程通过fd来找到这个文件,并且读取缓冲区文件内容,反之亦然,从而实现了进程间的通信,这就是管道的雏形,并且由此衍生出了匿名管道与命名管道

2.匿名管道

1.原理

看上图,父进程由*files指向files_struct,也就是文件描述符表,现在我们打开一个文件,fd = 3,对于打开的文件,该文件内容被存放在array[fd]中,我们创建一个子进程,子进程继承父进程的*files,files_struct等等,此时父子进程的指针指向同一块地方,也就是被打开的文件,于是父子进程同时可以访问该文件的缓冲区,或许你会疑问,被打开文件最后不是还要写回磁盘吗,其实真正的管道是由OS创建的一块内存,不需要刷回磁盘,和磁盘没关系,这也符合通信间的本质!!

所以真实的底层差不多是上图的样子,父进程使用pipe函数生成内存级通道,创建的子进程也可以通过fd指向此通道,此时关闭父进程的fd3与子进程的fd4,这就是进程之间的通信--单向通信,而pipe过程不需要文件路径,只需要一个pipefd[2]数组,没有文件名,所以此过程被称为匿名通信

2.pipe函数

通过man 2 pipe可以查看该系统函数

成功创建通道返回0,反之-1

对于成功创建的管道,pipefd[2]应该保存fd可读fd可写

3.pipe函数简单运用

testPipe.cc:

cpp 复制代码
#include <iostream>
#include <unistd.h>

int main()
{   
    int pfd[2] = { 0 };
    int n = pipe(pfd);
    if (n == -1)
    {
        std::cerr <<  "pipe error!" << std::endl;
        return 1;
    }
    std::cout << "pfd[0] : " << pfd[0] << std::endl;
    std::cout << "pfd[1] : " << pfd[1] << std::endl;
    return 0;
}

运行结果如下:

为什么是3与4呢,因为fd = 0,1,2已经被标准输入输出错误占据了

3.实现一个简易的父子进程管道通信

testPipe.cc:

cpp 复制代码
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <cstring>

void ChildWrite(int wfd)
{
    char buffer[1024];
    int cnt = 0;
    while (true)
    {
        buffer[0] = '\0';
        snprintf(buffer, sizeof(buffer), "我是子进程, pid : %d, cnt : %d", getpid(), cnt++);
        write(wfd, buffer, strlen(buffer));
        sleep(1);
    }
}

void FutherRead(int rfd)
{
    char buffer[1024];
    int cnt = 0;
    while (true)
    {
        buffer[0] = '\0';
        ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = '\0';
            std::cout << buffer << std::endl;
        }
    }
}

int main()
{
    // 创建管道
    int pfd[2] = {0};
    int n = pipe(pfd);
    if (n == -1)
    {
        std::cerr << "pipe error!" << std::endl;
        return 1;
    }
    std::cout << "pfd[0] : " << pfd[0] << std::endl;
    std::cout << "pfd[1] : " << pfd[1] << std::endl;
    // 一般来说,pfd[0] -> 读端   pfd[1] -> 写端
    // 创建子进程
    pid_t id = fork();
    if (id == 0)
    {
        // 关闭子进程读端
        close(pfd[0]);

        // 子进程向管道写入内容
        ChildWrite(pfd[1]);

        // 关闭子进程写端
        close(pfd[1]);
    }
    // 关闭父进程写端
    close(pfd[1]);

    // 父进程读取管道内容
    FutherRead(pfd[0]);

    // 等待子进程结束
    waitpid(id, nullptr, 0);

    // 关闭父进程读端
    close(pfd[0]);
    return 0;
}

运行结果如下:

好啦,这就是有关匿名通道的知识啦,我们之后博客会简绍另一个通道--命名通道哦,敬请期待!!

相关推荐
lengjingzju2 小时前
一网打尽Linux IPC(一):进程间通信完全指南——总体介绍
linux·服务器·c语言
阿豪学编程2 小时前
【Linux】进程信号深度解析
linux·运维·服务器
10000hours3 小时前
【Vim】vim常用命令:查找&编辑&可视区块
linux·编辑器·vim
chenyuhao20243 小时前
Linux网络编程:HTTP协议
linux·服务器·网络·c++·后端·http·https
广东大榕树信息科技有限公司3 小时前
动环监控如何有效提升机房环境管理的可靠性与响应速度?
运维·网络·物联网·国产动环监控系统·动环监控系统
txzz88883 小时前
CentOS-Stream-10 搭建NTP服务器(一)
linux·服务器·centos·ntp服务
冉佳驹4 小时前
Linux ——— 虚拟地址、页表、物理地址与 waitpid 和进程管理中的核心概念和技术
linux·waitpid·进程程序替换·exit·地址空间·非阻塞轮询·exec系列
先跑起来再说4 小时前
Go 语言的 Mutex 底层实现详解:状态位、CAS、自旋、饥饿模式与信号量
服务器·后端·golang
最后一个bug5 小时前
CPU的MMU中有TLB还需要TTW的快速查找~
linux·服务器·系统架构