当前位置: 首页 > article >正文

管道(Linux)

文章目录

  • 前言
  • 管道
    • 1.匿名管道
    • 2.指令管道
    • 3.有名管道
  • 总结


前言

进程通信是什呢???把一个进程的代码和数据交给另一个进程,而这可以访问同样的代码和数据。
我们可不可以直接让两个进程进行通信呢??不可以,因为进程具有独立性。
为啥子要进行进程通信呢??
🌟数据传输:一个进程需要将自己的代码和数据交给另一个进程,比如:一个进程读数据,一个进程写数据
🌟资源共享:让不同进程看到同样的数据,比如王者中的地图。
🌟获取数据:一个进程希望获取另一个进程的信息,比如父子之间,父进程等待子进程。
🌟进程控制:一个进程可以控制另外一个进程,让他执行相应的任务,或者在他出错时杀掉这个进程。
怎摸样进行通信呢??
通信的数据需要一块空间进行通信,这块空间是由操作系统提供的
进程通信的本质:让不同的进程看到同一份代码和数据

管道

管道是一种Linux中最经典的通信方式。

1.匿名管道

原理实现
父进程要创建自己的task_struct ,同时里面有一个指向struct files_struct的指针,struct files_struct里卖弄有一个struct files*数组,里面存放很多file。
如果我们把一个文件file.c读一下,写一下,这时会存在两个file,而不是一个

在这里插入图片描述
父进程创建子进程,要继承父进程的代码和数据,子进程也会创建task_struct,struct files_struct,file,这三个的内容都是一样的,这样两个进程就可以看到同一份代码和数据。
在这里插入图片描述

父进程要以读写的方式打开一个文件,这个文件会有两个file对象,创建子进程,子进程也会拥有对这个文件的读写方法,通过关闭父进程的写方法,关闭子进程的读方法,就可以让二者实现进程间通信。

pipe系统调用

我们看一下这个函数
在这里插入图片描述
🌟参数:两个fd的组成的数组,这是一个输出型参数,fd[0]表示读端,fd[1]表示写端。
🌟返回值:成功返回0。失败返回-1,错误码被设置。
🌟系统会创建一个匿名的文件,这个文件不会将内容刷新到磁盘中,并且这个文件不会被存放在磁盘中,会随进程销毁而销毁。

在这里插入图片描述

pipe代码验证

#include <iostream>
using namespace std;
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>

void Write(int wfd)
{
    char buffer[64];
    int cnt = 0;
    while (1)
    {
        snprintf(buffer, sizeof(buffer), "i am child,cnt:%d\n", cnt);
        ssize_t n = write(wfd, buffer, sizeof(buffer));
        if (n < 0)
        {
            cerr << "write error:" << errno << "message:" << strerror(errno) << endl;
            break;
        }
        cnt++;
        sleep(1);
    }
}

void Read(int rfd)
{
    char buffer[64];
    while (1)
    {
        ssize_t n = read(rfd, buffer, sizeof(buffer));
        if (n < 0)
        {
            cerr << "read error:" << errno << "message:" << strerror(errno) << endl;
            break;
        }
        cout << "father mesaasge:" << buffer << endl;
    }
}
int main()
{
    // 创建管道
    int pipefd[2];
    int Pipe = pipe(pipefd);
    if (Pipe < 0)
    {
        cerr << "pipe error:" << errno << "message:" << strerror(errno) << endl;
        exit(1);
    }
    // 创建子进程
    pid_t fid = fork();
    if (fid < 0)
    {
        cerr << "fork error:" << errno << "message:" << strerror(errno) << endl;

        exit(1);
    }
    // 子进程
    if (fid == 0)
    {
        // 关闭读端
        close(pipefd[0]);
        // 执行写操作
        Write(pipefd[1]);
        exit(1);
    }

    // 父进程
    close(pipefd[1]);
    Read(pipefd[0]);

    return 0;
}

我们看一下运行结果
在这里插入图片描述
在这里插入图片描述

我们发现确实父子进程之间完成了通信。

匿名管道四种情况

🌟如果管道里面没有内容&&没有关闭写进程,读进程就会阻塞等待。等待管道中有数据
🌟如果管道被写满了&&没有关闭读进程,写进程会阻塞等待,等待管道中数据被读走
🌟对于写端,不写了&&关闭写段,读端会把管道中数据读完,如果读端返回0,表示管道内没有数据。
🌟对于读端,不读了&&关闭读端,OS会对写端发送信号(13),杀死这个进程

匿名管道特征

🌟匿名管道是自带同步机制的
🌟匿名管道是面向字节流的
🌟匿名管道用于有血缘关系的进程
🌟匿名管道的生命周期随进程,进程退出,管道结束
🌟管道只能进行单通道通信,如果想要双通道,就要创建两个管道。
🌟当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性

2.指令管道

我们之前学习的 | 这本质也是管道,就像下面这种例子
在这里插入图片描述
这也属于匿名管道的一种,只不过这种是兄弟间进程通信
我们在执行上面操作的时候,OS会给我们创建两个进程和一个通道。

3.有名管道

如果我们想要实现几个没有关系的进程之间通信,就要使用有名管道。
原理实现

在这里插入图片描述
我们可以创建一个特殊的管道文件,这个管道是有名字的。这个文件与正常文件没有太大差别。多个进程同时对这个文件进程操作,我们就可以看到相同的资源,而进行进程间通信。
我们是可以保证多个进程看到的是同一个管道,这是通有名管道的文件名和路径唯一确定的。

实现指令管道

我们可以是由指令创建管道---->mkfifo
在这里插入图片描述

实现代码管道

这里是引用
第一个参数是文件名,第二个参数是文件权限。

client.cpp

#include "fifo.hpp"

int main()
{
    int wfd=open(PATH,O_WRONLY);
    if(wfd<0)
    {
            cerr<<"open fail"<<errno<<"message:"<<strerror(errno)<<endl;
    }
    //写内容
   string str;
    while(1)
    {
        cout<<"please enter you message:"<<endl;
        getline(cin,str);
        if(str=="quit") 
        {
            cout<<"quit client"<<endl;
        }
        int n=write(wfd,str.c_str(),str.size());
        if(n<0)
        {
            cerr<<"write fail"<<errno<<"message:"<<strerror(errno)<<endl;
        }

    }
    close(wfd);
    return 0;
}

server.cpp

#include "fifo.hpp"

int main()
{
    //创建管道
    Fifo fifo(PATH);
    //打开文件,进行读
    int rfd=open(PATH,O_RDONLY);
    if(rfd<0)
    {
            cerr<<"open fail"<<errno<<"message:"<<strerror(errno)<<endl;
    }
    char buffer[64];
    while(1)
    {
        ssize_t n=read(rfd,buffer,sizeof(buffer));
        if(n<0)
        {
            cerr<<"read fail"<<errno<<"message:"<<strerror(errno)<<endl;
            break;
        }
        else if(n>0)
        {
            cout<<"client say:"<<buffer<<endl;
        }
        else 
        {
            cout<<"quit serve"<<endl;
            break;
        }
    }
    close(rfd);
    return 0;
}

fifo.hpp

#pragma once

#include <iostream>
using namespace std;
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <string>
#include <unistd.h>
#include <fcntl.h>
#define PATH "fifo"

class Fifo
{
public:
    Fifo(const string&path)
    :_path(path)
    {
        umask(0);
        //创建匿名管道
        int ret=mkfifo(_path.c_str(),0666);
        if(ret==0)
        {
            cout<<"mkfifo success\n"<<endl;
        }
        else
        {
            cerr<<"mkfifo fail"<<errno<<"message:"<<strerror(errno)<<endl;
        }

    }
    ~Fifo()
    {
        int ret=unlink(_path.c_str());
        if(ret==0)
        {
            cout<<"unlink success"<<endl;
        }
        else
        {
            cerr<<"unlink fail"<<errno<<"message:"<<strerror(errno)<<endl;
            
        }
    }

private:
    string _path;
};

运行看一下实现情况

在这里插入图片描述

有名管道和匿名管道的特征和情况基本类似,只不过这个可以用于没有关系的进程

进程池补充

操作系统一次要创建多个进程进行管理,我们称为进程池。
操作系统当然也要对这些进程进行控制,发送命令,就必须对每个进程创建一个管道进行通信。
OS也要对这些进程和管道进行管理,先描述,在组织。
对于每个进程,要尽量实现负载均衡。

总结

以上就是今天要讲的关于linux中管道内容。希望对大家的学习有所帮助,仅供参考 如有错误请大佬指点我会尽快去改正 欢迎大家来评论~~ 😘 😘 😘


http://www.kler.cn/a/611479.html

相关文章:

  • SpringCould微服务架构之Docker(2)
  • 使用Ollama(自定义安装位置)与RagFlow构建本地知识库
  • WPF 与 C# 融合开发:从基础到高级应用(一)
  • Mysql update更新数据执行流程
  • Maven工具学习使用(四)——仓库
  • 智能科技与美学融合,赵伟辰荣膺 2025 iF 设计大奖
  • 量化研究--小果聚宽交易系统上线高速服务器,提供源代码
  • C#从入门到精通(2)
  • Java在2025年的新趋势与应用
  • 【MVCC快照如何实现】
  • 【MySQL篇】DEPENDENT SUBQUERY(依赖性子查询)优化:从百秒到秒级响应的四种优化办法
  • MongoDB 与 Elasticsearch 使用场景区别及示例
  • STL之string
  • 鸿蒙 一多适配/屏幕适配/设备适配/分栏
  • Java共享自习室系统全方位技术解析与实战搭建指南
  • C++:函数(通识版)
  • Ansys Zemax | 联合Speos实现供应商与OEM交换黑盒光学系统
  • AI编程工具-(九)
  • 双指针---《移动零》
  • 洛谷题单1-B2002 Hello,World!-python-流程图重构