Linux进程间通信之匿名管道

文章目录

  • 为什么要有进程间通信
  • pipe函数
  • 共享管道原理
  • 管道特点
  • 管道的应用场景(进程池)
    • ProcessPool.cc
    • Task.hpp

为什么要有进程间通信

数据传输:一个进程需要将它的数据发送给另一个进程
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

pipe函数

通过pipe函数实现两个进程间的通信
在这里插入图片描述
pipe()函数作用:生成两个文件描述符,分别为读端和写端
参数:
在这里插入图片描述

输出型参数pipefd[2],返回值pipefd[0]为读端,pipefd[1]为写端
返回值:端
成功返回0,失败返回-1,并且设置错误码error
在这里插入图片描述

共享管道原理

通过fork函数实现父子之间的管道共享,同一进程fork出的多个进程之间都可以进行管道共享,因此只要是亲戚就可以。
管道共享更确切的说应该是缓冲区共享,我们先来理解一下fork函数,一个进程fork出了子进程,两个进程之间的代码是共享的,数据是独有的,当其中一个进程的数据发生改变时,就会发生写时拷贝。那么文件缓冲区呢?
父子之间的文件缓冲区也是共享的,因此父子之间就是借助这一点进行通信的。
我们以3号文件描述符为读端,4号文件描述符为写端为例,父进程向3号文件描述符写,子进程将数据写入到4号文件描述符。而4号文件描述符读到的就是父进程向3号文件描述符写的数据,这是怎么实现的呢?
1.父子进程是同步的
2.父子之间缓冲区是共享的。
因此当父亲向缓冲区写的时候,子进程就直接从缓冲区内读
在这里插入图片描述
你可能会有疑问,操作系统为什么要搞出管道,要是上面那样的话,和父进程直接向一个文件写,子进程从这个文件里读有什么区别?
管道通信是加载在内存上的,管道本身是一块缓冲区,这种方式更快,因为对于文件而言,它是在磁盘上加载的,如果单纯的对一个文件进行读写操作,肯定是要慢一些的
为什么说这种管道通信只能应用于亲戚之间呢?
因为只有亲戚之间,也就是同一个进程fork出的进程之间才会进行缓冲区共享

#include <iostream>
#include <unistd.h>
#include <error.h>
#include <stdio.h>
#include <cstring>
#include <sys/wait.h>

#define N 2
#define NUM 1024

using namespace std;

void Writer(int wfd)
{
    string s = "i am a child abcdefg";
    char buf[NUM];
    buf[0] = 0;
    snprintf(buf, sizeof(buf), "%s", s.c_str());//把s.c_str()以字符串形式写入到buf里
    write(wfd, buf, sizeof(buf));
    //write(wfd, buf, sizeof(s.c_str()));
    // cout << "sizeof(s.c_str()):" << sizeof(s.c_str()) << endl;//s.c_str()返回值为char类型的指针
    // cout << "strlen(buf):" << strlen(buf) << endl;
    // cout << "strlen(s.c_str()):" << strlen(s.c_str()) << endl;
}

void Reader(int rfd)
{
    char buf[NUM];
    ssize_t n = read(rfd, buf, sizeof(buf));//sizeof != strlen
    buf[n] = 0;//0 == '\0' 
    cout << buf << endl;
    //cout << n << endl;
    //printf("%s\n", buf);
}

int main()
{
    int pipefd[N] = {0};//pipefd[2]
    int n = pipe(pipefd);//给pipe()函数传递一个数组,返回的数组下标0位置是读的文件描述符,下标1位置为写的文件描述符
    //cout << pipefd[0] << " " << pipefd[1] << endl;
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork error");
        return 1;
    }

    if(id == 0)//child --- 我们让子进程写,父进程读
    {
        close(pipefd[0]);//关闭子进程的读文件描述符
        Writer(pipefd[1]);
        close(pipefd[1]);//可关可不关,因为进程结束,它会自动关闭
        exit(0);
    }

    if(id > 0)//father----我么让父进程读,子进程写
    {
        close(pipefd[1]);//关闭父进程的写文件描述符
        Reader(pipefd[0]);   
    }
    pid_t rid = waitpid(id, nullptr, 0);//return id -------- rid
    close(pipefd[0]);//可关可不关,因为进程结束,它会自动关闭
    return 0;
}

管道特点

1.只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
2.管道提供流式服务
3.一般而言,进程退出,管道释放,所以管道的生命周期随进程
4.一般而言,内核会对管道操作进行同步与互斥管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
在这里插入图片描述

管道的应用场景(进程池)

我们知道当一个进程要执行一个事情时,一般它会创建一个子进程,并把这件事交给该进程让它去完成,现在我们有若干个任务要去让这个进程去完成,因此该进程就要去创建多个子进程,让他们分别去完成这些事,但是像这种每一次都要创建子进程的过程是很浪费时间的,操作系统是不会允许这种影响效率的事情发生的,那么我们要怎么提高效率呢?
进程池,就是让为该进程提前创建好若干个子进程,当有多个任务来的时候,就让这个父进程给子进程去派发不同的任务。我们以父进程为老板,子进程为打工人的场景来模拟,具体实现如下:

ProcessPool.cc

#include <unistd.h>
#include <string>
#include <iostream>
#include <vector>
#include <sys/wait.h>
#include <ctime>
#include "Task.hpp"

using namespace std;

#define processnum 10
#define N 2
#define NUM 1024
vector<task_t> tasks;//声明Task.hpp中的变量

//先描述
class channel//管道
{
public:
    channel(int cmdfd, pid_t slaverid, const string& processname)
        :_cmdfd(cmdfd)
        ,_slaverid(slaverid)
        ,_processname(processname)
    {

    }

public:
    int _cmdfd;//发送任务的文件描述符
    pid_t _slaverid;//该子进程的pid
    string _processname;//子进程的名字
};

void slaver()
{
    // char buf[NUM];
    // read(0, buf, sizeof(buf));
    
    int cmdnum = 0;//几号任务
    read(0, &cmdnum, sizeof(int));
    //cout << "读到了:" << cmdnum << endl;
    if(cmdnum > 0 && cmdnum <= tasks.size())
    {
        //cout << "cmdnum:" << cmdnum << endl;
        tasks[cmdnum - 1]();//为什么要加括号?
        //cout << "读到了:" << cmdnum << endl;
    }
    
}

void Menu()
{
    cout << "*******************************" << endl;
    cout << "********1.开机    2.打怪兽******" << endl;
    cout << "********3.回血    4.关机********" << endl;
    cout << "*******************************" << endl;
    cout << "请输入要执行的任务" << endl;
}


void InitChannels(vector<channel>* channels)
{
    for(int i = 0; i < processnum; i++)
    {
        int pipefd[N] = {0};
        pipe(pipefd);
        //cout << "pipefd[0]:" << pipefd[0] <<  "   " << "pipefd[1]:" << pipefd[1] << endl;
        pid_t pid = fork();
        if(pid == 0)
        {
            close(pipefd[1]);
            dup2(pipefd[0], 0);
            slaver();
            //slaver(pipefd[0]);
            //close(pipefd[0]);//子进程读的文件描述符可以不用关
            exit(0);
        }
        

        //father
        close(pipefd[0]);
        //write(pipefd[1], "abcd", sizeof("abcd"));
        //Writer();
        string name = "process:" + to_string(i);
        channels->push_back(channel(pipefd[1], getpid(), name));
        //close(pipefd[1]);
        //waitpid(getpid(), nullptr, 0);

    }   
}

void Print(vector<channel> channels)
{
    int i = 0;
    for(auto& e : channels)
    {
        cout << e._cmdfd << " " << e._processname << " " << e._slaverid << endl;
        //cout << "xxxxxxxxxxxxxxxxxxx" << i << "xxxxxxxxxxxxxxxxxxxxx" << endl;
        i++;
    }
}

void ctrlSlaver(vector<channel> channels)
{
    while(1)
    {
        //1.选择任务
        Menu();
        int select = 0; 
        cin >> select;
        //2.选择进程
        srand(time(nullptr));
        int processpos = rand() % channels.size();//进程vector中对应的下标位置
        //3.发送任务
        write(channels[processpos]._cmdfd, &select, sizeof(int));
        //cout << channels[processpos]._cmdfd << endl;
        sleep(1);
    }
}

void QuitProcess(const std::vector<channel> &channels)
{
    for(const auto &c : channels) close(c._cmdfd);
    // sleep(5);
    for(const auto &c : channels) waitpid(c._slaverid, nullptr, 0);
    // sleep(5);
}



int main()
{
    LoadTask(&tasks);
    vector<channel> channels;
    //1.初始化channels
    InitChannels(&channels);
    //Print(channels);
    //2.控制子进程
    ctrlSlaver(channels);
    QuitProcess(channels);
    return 0;
}

Task.hpp

#pragma once

#include <iostream>
#include <vector>

using namespace std;

typedef void (*task_t)();//task_t先和*结合,所以task_t是一个指向参数为空,返回值为void的函数指针

void task1()
{
    cout << "开机" << endl;
}

void task2()
{
    cout << "打怪兽" << endl;
}

void task3()
{
    cout << "回血" << endl;
}

void task4()
{
    cout << "关机" << endl;
}

void LoadTask(vector<task_t> *tasks)
{
    tasks->push_back(task1);
    tasks->push_back(task2);
    tasks->push_back(task3);
    tasks->push_back(task4);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/164207.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FlinkCDC数据实时同步Mysql到ES

考大家一个问题&#xff0c;如果想要把数据库的数据同步到别的地方,比如es,mongodb,大家会采用哪些方案呢&#xff1f; ::: 定时扫描同步&#xff1f; 实时日志同步? 定时同步是一个很好的方案&#xff0c;比较简单&#xff0c;但是如果对实时要求比较高的话&#xff0c;定…

JAVAEE---计算机是如何组成的

计算机软件硬件 硬件是冯诺依曼体系结构&#xff0c;这个结构的精髓在于将存储和执行分开。 这里存储器内存外存&#xff08;硬盘&#xff0c;u盘&#xff0c;光碟等&#xff09; cpu是计算机的大脑&#xff0c;是计算机最核心的地方。 cpu中央处理&#xff1a;进行算术运算…

ESP32 Arduino实战协议篇-搭建独立的 Web 服务器

在此项目中,您将创建一个带有 ESP32 的独立 Web 服务器,该服务器使用 Arduino IDE 编程环境控制输出(两个 LED)。Web 服务器是移动响应的,可以使用本地网络上的任何浏览器设备进行访问。我们将向您展示如何创建 Web 服务器以及代码如何逐步工作。 项目概况 在直接进入项目…

03. Python中的语句

1、前言 在《Python基础数据类型》一文中&#xff0c;我们了解了Python中的基础数据类型&#xff0c;今天我们继续了解下Python中的语句和函数。 2、语句 在Python中常用的语句可以大致分为两类&#xff1a;条件语句、循环语句。 2.1、条件语句 条件语句就是我们编码时常见…

Redis篇---第八篇

系列文章目录 文章目录 系列文章目录前言一、说说 Redis 哈希槽的概念?二、Redis 常见性能问题和解决方案有哪些?三、假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?前言 前些天发现了一个巨牛的人工智能学习网站…

基于世界杯算法优化概率神经网络PNN的分类预测 - 附代码

基于世界杯算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于世界杯算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于世界杯优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络…

Linux基础全整理 从入门到放弃,一些想说的话

阅读目录 断更后一些想说的话用户useraddpasswdpasswd文件详解 chageusermoduserdelshadow 文件格式切换用户 用户组groupaddgroup文件格式groupmodgroupdel登陆远程机器 磁盘RAIDraid0&#xff08;安装系统&#xff09;raid1&#xff08;存放数据&#xff09;raid 5&#xff0…

Prompt 编程的优化技巧

大家好&#xff0c;我是木川 一、为什么要优化 一&#xff09;上下文限制 目前 GPT-3.5 以及 GPT-4最大支持 16K 上下文&#xff0c;比如你输入超过 16k 的长文本&#xff0c;ChatGPT 会提示文本过大&#xff0c;为了避免 GPT 无法回复&#xff0c;需要限制 上下文在16k 以内 上…

【数据结构算法(一)】递归篇(常见实例讲解)

&#x1f308;键盘敲烂&#xff0c;年薪30万&#x1f308; ⭐本篇讲解实例&#xff1a; 斐波那契、兔子问题、猴子吃桃问题、跳台阶问题、汉诺塔、杨辉三角 ⭐用到的递归思想&#xff1a; 无记忆递归、记忆递归(重点掌握) 目录 一、斐波那契&#xff1a; ①无记忆多路递归&am…

重生奇迹mu转职任务详解

重生奇迹mu神骑士怎么转 神骑士是一种转职类型&#xff0c;需要你的角色达到一定等级以及完成相应任务方可转职。以下是神骑士转职的具体步骤&#xff1a; 1.等级要求&#xff1a;首先&#xff0c;你的角色需要达到150级才能进行神骑士转职任务。 2.神骑士转职任务&#xff…

十七、Linux的组管理

1、Linux组基本介绍 在linux中的每个用户必须属于一个组&#xff0c;不能独立于组外。在linux中每个文件所有者、所在组、其它组的概念 1.所有者 2.所在组 3.其他组 4.改变用户所在的组 2、文件/目录 所有者 一般为文件的创建者&#xff0c;谁创建了该文件&#xff0c;就自…

卷积、卷积图像操作和卷积神经网络

好多内容直接看书确实很难坚持&#xff0c;就比如这个卷积&#xff0c;书上的一大堆公式和图表直接把人劝退&#xff0c;我觉得一般的学习流程应该是自顶向下&#xff0c;先整体后局部&#xff0c;先把握大概再推敲细节的&#xff0c;上来就事无巨细地展示对初学者来说很痛苦。…

【机器学习12】集成学习

1 集成学习分类 1.1 Boosting 训练基分类器时采用串行的方式&#xff0c; 各个基分类器之间有依赖。每一层在训练的时候&#xff0c; 对前一层基分类器分错的样本&#xff0c; 给予更高的权重。 测试时&#xff0c; 根据各层分类器的结果的加权得到最终结果。 1.2 Bagging …

Linux | 信号

目录 前言 一、信号基础概念 1、生活中的信号 2、Linux中的信号 二、信号的产生 1、接口介绍 2、信号产生的方式 &#xff08;1&#xff09;终端按键的方式产生信号 &#xff08;2&#xff09;系统调用接口 a、kill b、raise c、abort &#xff08;3&#xff09…

【LeetCode刷题-滑动窗口】--992.K个不同整数的子数组

992.K个不同整数的子数组 思路&#xff1a; class Solution {public int subarraysWithKDistinct(int[] nums, int k) {return atMostKDistinct(nums,k) - atMostKDistinct(nums,k-1);}//最多包含K个不同整数的子区间个数private int atMostKDistinct(int[] a,int k){int len …

【MATLAB源码-第83期】基于matlab的MIMO中V-BALST结构ZF和MMSE检测算法性能误码率对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 在多输入多输出&#xff08;MIMO&#xff09;通信系统中&#xff0c;V-BLAST&#xff08;垂直波束形成层间空间时间编码技术&#xff09;是一种流行的技术&#xff0c;用于提高无线通信的数据传输速率和容量。它通过在不同的…

PS 颜色取样器标尺工具 基本使用讲解

上文 PS 吸管工具基本使用方法 我们讲完了 吸管工具 那么 我们继续 打开ps先 接着 我们选择这个 颜色取样器工具 选择之后 我们鼠标在图像上随便点一下 就会出现一个标记 然后 我们可以点多几个地方 边上的信息面板就会输出 点1 和 点2 甚至 多个 点3 点4 的 颜色 RGB代码 …

Python学习(一)基础语法

文章目录 1. 入门1.1 解释器的作用1.2 下载1.3 基础语法输入输出语法与引号注释&#xff1a;变量&#xff1a; 数据类型与四则运算数据类型四则运算数据类型的查看type()数据类型的转换int()、int()、float() 流程控制格式化输出循环与遍历逻辑运算符list遍历字典dict遍历 跳出…

JavaspringbootMYSQL基于移动端的团购网站26449-计算机毕业设计项目选题推荐(附源码)

目 录 摘要 1 绪论 1.1 选题背景 1.2选题目的及意义 1.3springboot框架介绍 2 基于移动端的团购网站系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 业务流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章…
最新文章