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

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

1.什么是管道

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

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

2.匿名管道

1.原理

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

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

2.pipe函数

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

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

对于成功创建的管道,pipefd2应该保存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;
}

运行结果如下:

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

相关推荐
Web3探索者8 小时前
可视化服务器管理和传统命令行区别是什么?新手教程:Linux 运维到底该用图形界面还是 SSH 命令行?
linux·ssh
zylyehuo10 小时前
Linux系统中网线与USB网络共享冲突
linux
荣--13 小时前
一键部署不是为了省时间 —— 它是把"买来的 PaaS"变成"自己的平台"的拐点
运维·zabbix·工程化·一键部署·平台化·边界设计
江华森13 小时前
动手实战学 Docker — 从零到集群编排完全指南
运维
Avan_菜菜1 天前
FRP 内网穿透完整实战:从 HTTP 映射到 HTTPS 自签代理
运维·nginx·https
Sokach10152 天前
Linux Shell 脚本从零到能用:一个新手的一天学习总结
linux
SelectDB2 天前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
AlfredZhao2 天前
Docker 容器时区不对,`timedatectl` 不存在怎么办?
linux·timezone
zzzzzz3104 天前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql
XIAOHEZIcode4 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏