Linux:匿名管道的四种情况(进程间通信四)

了解了匿名管道的五种特性后,我们必须得谈谈匿名管道的四种情况--快读慢写,快写慢读,只写不读,只读不写,接下来我将一一讲述,话不多说现在开始

1.快读慢写

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

实验结果--读端阻塞,等待写端

2.快写慢读

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)
    {
        sleep(5);
        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;
}

实验结果--写端阻塞,等待读端

3只写不读

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);
        //break;
    }
}

void FutherRead(int rfd)
{
    char buffer[1024];
    int cnt = 0;
    while (true)
    {
        //sleep(5);
        buffer[0] = '\0';
        ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = '\0';
            std::cout << buffer << std::endl;
        }
        /*
        else if (n == 0)
        {
            std::cout << "没有内容 : " << n << std::endl;
            std::cout << "子进程退出,我也退出" << n << std::endl;
            break;
        }
        else
        {
            break;
        }*/
       break;
    }
}

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]);

    //验证只写不读,提前关闭读端
    close(pfd[0]);

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

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

OS始终想着怎么优化,所以如果只写不读,那么就没有任何意义,操作系统不会做这种没有意义的事情,会直接杀死进程,下面我们来验证一下

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);
        //break;
    }
}

void FutherRead(int rfd)
{
    char buffer[1024];
    int cnt = 0;
    while (true)
    {
        //sleep(5);
        buffer[0] = '\0';
        ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = '\0';
            std::cout << buffer << std::endl;
        }
        /*
        else if (n == 0)
        {
            std::cout << "没有内容 : " << n << std::endl;
            std::cout << "子进程退出,我也退出" << n << std::endl;
            break;
        }
        else
        {
            break;
        }*/
       break;
    }
}

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]);

    //验证只写不读,提前关闭读端
    close(pfd[0]);

    // 等待子进程结束
    //waitpid(id, nullptr, 0);
    int status = 0;
    waitpid(id, &status, 0);
    std::cout << "exit code : " << ((status >> 8) & 0xFF) << std::endl;
    std::cout << "exit signal : " << (status & 0x7F) << std::endl;

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

此时退出码为0,表示无意义,退出信号为13,符合预期

4.只读不写

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);
        break;
    }
}

void FutherRead(int rfd)
{
    char buffer[1024];
    int cnt = 0;
    while (true)
    {
        //sleep(5);
        buffer[0] = '\0';
        ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = '\0';
            std::cout << buffer << std::endl;
        }
        else if (n == 0)
        {
            std::cout << "没有内容 : " << n << std::endl;
            std::cout << "子进程退出,我也退出" << n << std::endl;
            break;
        }
        else
        {
            break;
        }
    }
}

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

只读不写场景是指读进程持续读取数据,但写进程停止写入且关闭了写端 ,此时读端会读取完管道中剩余数据,随后检测到 "文件结束(EOF)",read() 返回 0,读进程可正常退出

5.四种场景核心结论与特性总结

通过对四种场景的分析,我们可以归纳出匿名管道的三大核心特性,以及对应的场景体现:

匿名管道特性 对应场景 核心表现
自带同步机制 快读慢写、快写慢读 读端空则阻塞读进程,写端满则阻塞写进程
内核缓冲区有容量限制 快写慢读 写端写入速度过快时,缓冲区满后阻塞写进程
半双工 + 面向字节流 所有场景 数据单向传输,无消息边界,读端按字节读取
EOF 检测机制 只读不写 所有写端关闭后,读端 read() 返回 0
异常场景信号终止 只写不读 读端关闭后,写端收到 SIGPIPE 信号

好啦,这就是有关匿名管道通信的四种情况啦,希望大家可以多多看哦~~

相关推荐
其实防守也摸鱼4 小时前
推荐一个自动化教育SRC漏洞挖掘系统--AutoHunter
运维·开发语言·人工智能·学习·安全·web安全·自动化
九硕智慧建筑一体化厂家4 小时前
智慧建筑高效管控!楼宇自控系统赋能新能源园区智能运维
运维·人工智能·笔记·智慧城市
咯哦哦哦哦5 小时前
linux so库(arm)对比分析
linux·运维·arm开发
lengjingzju10 小时前
编译与调试完全指南—第10章 网络分析工具
linux
星野川崎20611 小时前
电商多店运维:云机24小时挂机频繁掉线、账号无故风控深度原因分析及解决方案
大数据·运维·服务器·云计算·电商
ltl11 小时前
新硬件对存储的影响:ZNS、CXL 与计算存储
linux
RisunJan11 小时前
Linux命令-xauth(X11 认证授权管理)
linux·服务器·microsoft
熊IT13 小时前
什么是原生住宅 IP?如何判断一个 IP 是否真正“原生”?
服务器·网络·tcp/ip
不灭的程序员阿澄13 小时前
在飞牛 NAS 上用 Docker 运行 DeepSeek Harness
运维·docker·容器
Elastic 中国社区官方博客13 小时前
让大模型思考,让小模型执行:在 Elastic Workflows 中拆分 LLM 成本
大数据·运维·数据库·人工智能·elasticsearch·ai