【Linux系统编程十九】:(进程通信)--匿名管道/模拟实现进程池

news2024/11/23 8:49:53

【Linux系统编程十九】:匿名管道原理/模拟实现进程池

  • 一.进程通信理解
  • 二.通信实现原理
  • 三.系统接口
  • 四.五大特性与四种情况
  • 五.应用场景--进程池

一.进程通信理解

什么是通信?

通信其实就是一个进程想把数据给另一个进程,但因为进程具有独立性,想直接给是不行的。
所以就必须得要有通信的方案。

为什么要通信?
主要是要么是传数据,要么传指令,要么是我们进行多进程间的协同,要么是可能是一个进行想通知另一个地方某些事情发生了。
啊,不管什么原因,反正就是要通信。

怎么通信?
但通信的时候呢,那么对我们来讲呢,竞争具有独立性,那通信就有成本,那怎么办呢?
所以通信它的实现方案,那么这里呢本质就一定是先要要让不同的进程先看到同一份公共的资源。
这份公共资源不能属于我们通信的进程当中的任何一个。
啊,因为竞争具有独立性,如果属于任何一个的话,那这个资源就不应该让其他竞争看到。
所以这个资源呢就必须只能由操作系统提供。
一般而言必须只能由操作系统提供。
而我们对应的呃就是操作系统提供的资源呢,是不允许任何进程直接去访问的。
啊,所以就注定了我们一定要提供大量的我们关于就业通信的相关的系统调用接口。
所以从底层设计上呢,我们的操作系统既要给我们提供通信的方案,又要给我们提供那么进行通信让用户所调用的接口。
如果我们一个文件,它能够被多个进程打开并访问。
那么我们文件呢,这玩意儿就不就是一个公共资源了吗?
等一个进程往文件里写,另一个进程从文件里读。
只要你写完之后把数据直接刷新,刷新到磁盘上,另一个我们对应的这个进程再从文件里读,不就读到数据了吗?
好,同学们,那么这种呢那么思想呢其实对的吗?
那么所谓的管道呢,其实就是基于文件的一种通信方式。
他的思想呢其实跟我刚刚说的那个其实大差不差,只不过呢他并不把数据往磁盘当中做刷新。
谁规定数据一定要刷到磁盘上?
那么管道通信时,只要建立好双方的通信信道,往里写,你去读就可以。
不一定非得用我们对应的文磁盘呀,我们用内存也可以。
在这里插入图片描述

二.通信实现原理

我们每一个文件呢,它都天然的要提供一个属于自己对应的,叫做页缓冲区。全称叫做文件页缓区,我们叫做缓冲区。

从理论上来讲呢,如果这个文件就是一个普通文件,我们这个文件一定是曾经要么在在磁盘当中被新建的,要么在我们磁盘当中被打开的。
反正我们最终那么一个文件在磁盘当中,假设它也存在的话,它会在特定的分区,特定的分组,有自己的属性和数据块。在操作系统内核当中,它会存在非常非常多的内存级文件。
也就是这样的文件呢,我们并不需要在我们对应的磁盘当中真正的存在。
啊,那么而是呢最终只要能够在内存里让我们那么能够把它用起来就可以。我们只需要把我们曾经学到的知识里面不要做刷新,那么剩下的这不就是内存级文件吗

在这里插入图片描述
让进程打开一个文件时,再创建一个子进程。子进程会进程父进程的PCB,页表,地址空间。但文件不会继承。
这样父进程里的文件描述符表和子进程的文件描述符表都指向一样的文件。
刚刚新建的这个内存及文件。父子进程是不是都可以访问,都可以被父子进程看到。
还记得进程间通信的本质吗:让不同的进程看到同一份资源。这不就做到了吗。

进程先打开文件,在fok之后创建子进程进程。 此时如果我们有能力打开这种文件的话,那么此时它不就叫做父进程程和子进程看到了同一个文件吗?那么这个同一个文件它是内存级的。因为每个文件都存在自己对应的缓冲区。
双方就可以实现进程间通信了。

在系统当中父进程在打开我们对应的一个文件的时候呢。
它并不是只是单方面的去把一个文件以读写方式打开,或读或写都不是。他在创建这个管道时,把同一个文件既以读方式打开,又以写方式打开。
所以父进程打开文件的时候,以读写方式整体把管道文件打开。
那么接下来呢我们的父进程呢在fork创建出子进程后。
子进程它会拷贝父进程的文件描述符表,所以它们两个当中父进程和子进程都会有对应的读写端。指向同一个缓冲区
在这里插入图片描述

好,这就是我们的管道啊。
至于父进程是读,子进程写,还是父进程是写,子进程是读,需要用户来决定?
只要让父进程和子进程它们各自要关闭对应的读写端,来形成一个叫做单向通信的信道。比如说图当中呢它是想要父进程进行写入。子进程读取,只要将父进程就把曾经自己的读端这个关掉。对应的子进程呢,它想对我们对应的管道来进行读取读取,所以他就把自己的写入关掉。
关掉了这次我们的父子进程就可以使用,父进程剩下一个写端,子进程剩下一个读端,那么建立了这样的一种通信信道。
在这里插入图片描述
这种基于文件级别的通信方式,那么正是因为它只能进行单向通信。所以我们命名为叫做管道。

以上所做的工作叫做建立通信信道。现在父子进程还没进行通信。

三.系统接口

在这里插入图片描述
这个系统调用接口很简单,它的参数是一个数组,数组只有两个参数。而这个参数是作为输出型参数的。将文件的读写描述符带出来。默认pipefd[0]是读端,pipefd[1]是写端。
而这个系统调用所做的工作就是我们上面所讲的实现原理做的工作即建立信道。这个信道是由固定大小的,一般为64KB.
创建完信道后,我们还有件事情,就是要形成单向信道,这件事需要让父子进程共同完成,父进程需要读,就要手动将写端关闭。
子进程需要写,就要手动将读端关闭。
最基本的一个叫做我们建立单向信道的过程。建立完单向信道后,就可以往信道里写入信息了。写入信息时要注意管道本质上是文件。而文件本质上是内核资源。操作系统允不允许你的父进程和子进程直接去访问这个文件资源呢?只能通过系统调用接口去访问,所以只能使用write和read系统调用接口进行。

四.五大特性与四种情况

在这里插入图片描述
管道的特性有五个:
一:只有具有血缘关系的进程才可以进行通信。在这里插入图片描述

二:管道只能单向通信。
三:父子进程是会进行协同的。
四:管道是面向字节流的。
五:管道本质就是文件,属于内核资源,进程结束就会自动释放。

管道还会出现四种情况:
1.(写段写的速度比读端要慢)读写端都正常,管道如果为空,那么读端就要阻塞。
2.(写端写的速度比读端要快)读写端正常,管道如果被写满,写段就要阻塞。
3.(写端关闭)读端正常,写端关闭,读端就会读到0,表面读到了管道的结尾处,读端对应的进程并不会阻塞。
4.(读端关闭)写端正常,读端关闭,操作系统就要杀死正在写入的进程,因为没有意义。利用信号杀死。

五.应用场景–进程池

在shell里就存在管道,竖画线它就表示管道。
第一,当我用管道集连起对应的这若干个命令时。
那么其中每一个对应的命令最终都会被直接启动成一个进程。
也就是他们这些进程是同时被起来的啊,也就就是你在跑的同时我也在跑,只不过我在等你啊。这表面它们之间都是子进程。谁的子进程呢?bash的子进程,它们都是bash的子进程,所以可以利用管道。
两个竖画线分割三个命令。创建两个管道之后,然后连续再创建三个子进程,然后每个进程程序计划执行不同的命令。在执行之前呢,我们需要对每一个进程的标准输出啊标准输入进行一下重定向。每一个管道它都有自己的读端和写端。勾连起这几个命令,然后呢让他们直接互相通信起来。
第一个进程的标准输出重定向到我们管道的写端,中间进程的标准输入重定向到上一个管道的读端。表示我本来往显示器上进行打印,现在呢把显示器上打印改成向管道里去进行我们对应的写入。
中间有个进程呢,它呢本来读的时候是从我们对应的键盘读取,今天读的时候,我就不要让它从键盘读,而是从我们对应的管道文件里读,显示的时候就不用再写了,不用向显示器写了,而是继续向后面的管道再继续写入啊,所以做一堆的重定向。
最后一个进程的话,做一下输入重定向。这就是shell中的重定向实现原理。

管道还有一个应用场景:进程池。

我们能不能提前把一批任务先建立好,当有任务到来时,然后我直接指派给其中一个进程呢?。
其中我们一次把对应的一批进程直接创建好,那么这个工作我们就叫做我们先进行一次,叫做进程池的储备。
也就是把一个一个的进程呢当做一一份一份儿一份的资源,提前储备好,提前做个好。 当我们需要的时候再去指派,让他去帮我们去完成任务。

在这里插入图片描述

首先对应的父进程。在正式接受新需求,接受新任务之前。
啊,我先一次性同时创建出若干个子进程。然后呢为了后面呢更好的去控制上面的所有的子进程。
我呢想做这样的工作,为我的父进程想和其中我所创建的每一个子进程。都建立一条叫做管道的信道。
一个进程建立一个管道和第二个进程建立一个管道和第三个建立管道,第四个建立管道和第五个建立管道和第六个建立管道……,以此类推。
然后我们让每一个子进程只从管道当中进行读取。父进程呢它把控这批管道。那么父进程想那往哪个管道里写内容,他就可以直接向哪个管道里写内容。
那么其中我们对应的父进程,如果没有像第一个管道里写任何内容,请问这个子进程在干什么?
这个子进程它在读取等待我们管道流数据。
那么也就是说子进程当前就阻塞在这个管道当中,他就在等这个父进程给他任务呢。
那么父进程呢,不给你写,子进程就等着呗。
然后父进程一旦向管道当中写了,写了之后,那么这个进程会读到对应的数据,然后这个子进程会继续向后执行。向后我就提前可以让这个进场。那么结合他读进来的数据以及他向后执行这个动作,然后我们就可以让这个子进程上去执行对应的任务了。
好,换句话说,从此往后我的父进程,我们规定父进程向子进程当中管道里写的,我们把它都叫做一个一个的任务。(父子通信时,那么父进程每次写入时只能有写四个字节)
我们对应的父进程呢,他想布置任务的时候,他无非就是做两件事情。第一个叫做选择任务。第二个叫做选择进程。
好,也就是他把任务确定好了。第二他把进程确定好了,他就可以把这个任务指派给其中的某一个子进程去运行。


#include "TASK.hpp"
#include <iostream>
#include <string>
#include <unistd.h>
#include <vector>
#include <sys/wait.h>
#include <sys/stat.h>
#include <time.h>
#define N 5

std::vector<task_t> tasks;
//master----[]---slaver,master通过信道控制子进程
//先描述,再组织:这个信道是由管道和子进程的pid构成


class channel
{
public:
   channel(int cmdfd,pid_t slaverid,const std::string &processname)
   :_cmdfd(cmdfd)
   ,_slaverid(slaverid)
   ,_processname(processname)
   {}

public:
    int _cmdfd;//发送任务的文件描述符
    pid_t _slaverid;//子进程的pid
    std::string _processname;//子进程的名字
};
std::vector<channel> channels;
//在创建子进程之前创建管道,然后让父子进程分别关闭一端
void slaver()
{
    //子进程直接从标准输入里就可以获取到任务码,就没有管道的概念了
   while(1)
   {
     int cmdcode=0;//将任务码带出来
    int n=read(0,&cmdcode,sizeof(int));
    if(n==sizeof(int))
    {
       //根据任务码的不同执行不同的函数
       std::cout<<"slaver say@ get a command:"<<getpid()<<": code--> "<<cmdcode<<std::endl;    
       tasks[cmdcode]();
    }
    if(n==0)//说明读取完 ,没有可读的了,子进程就可以退出了
    break;
   }
  sleep(3);
}
void InitProcesspoll(std::vector<channel>* channels)//输出型参数用指针
{
 for(int i=0;i<N;i++)
    {
        int pipefd[2];
        pipe(pipefd);
        //创建管道
        pid_t id=fork();
        if(id==0)
        {
            close(pipefd[1]);//子进程,读,关闭写
            
            dup2(pipefd[0],0);//--重定向到标准输入
            slaver();//子进程执行任务,从信道里读取任务码,封装成一个函数,但这里我们可以重定向,向标准输入里读取

            exit(0);
        }
        
        close(pipefd[0]);//父进程写,关闭读
      //管道和子进程都创建好了,可以初始化信道了
      std::string name="process-"+std::to_string(i);
      channels->push_back(channel(pipefd[1],id,name));     
    }
}
void ctrlSlaver(std::vector<channel>& channels)//输入输出型参数用引用
{
   //接下来就是父进程控制子进程发送任务给子进程
    int cnt=5;
   while(cnt--)
   {
    
     //1.选择任务
    int cmdcode=rand()%tasks.size();
    
    //2.选择进程

    int processpos=rand()%channels.size();//可以通过这个位置找到信道,找到子进程

    std::cout<<"father say comcode:"<<cmdcode<<" have send to"<<channels[processpos]._slaverid<<"->"<<channels[processpos]._processname<<std::endl;
    

    //3.发送任务

    //选好子进程后,就可以往这个信道里发送任务
    write(channels[processpos]._cmdfd,&cmdcode,sizeof(int));
    sleep(2);
   }
}
void QuitProcess(const std::vector<channel>& channels)
{
 //如何关闭子进程?只要让写端关闭,读端读到0就自然会跳出循环
   for(auto& e: channels)
   {
      close(e._cmdfd);
   }
   sleep(5);
   //子进程跳出循环后就会退出,父进程等待子进程即可
   for(auto& e:channels)
   {
      waitpid(e._slaverid,nullptr,0);
   }
}
int main()
{
  
   srand(time(nullptr));//生成随机数
   
   LoadTask(&tasks);//1.把任务加载进来
   
   InitProcesspoll(&channels);//2.初始化-->创建管道,创建子进程,初始化信道。
  
   ctrlSlaver(channels); //3.开始控制子进程
   
   QuitProcess(channels);//4.清理收尾
  


}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小程序授权获取头像

wxml <view class"header"><text>头像</text><button class"butt" plain"true" open-type"chooseAvatar" bind:chooseavatar"chooseAvatar"><image src"{{HeadUrl}}" mode"&quo…

Java - 位运算的基本原理和用途

Java - 位运算的基本原理和用途 前言一. Java 位运算基本操作1.1 按位与 &1.2 按位或 |1.3 按位异或 ^1.4 按位取反 ~1.5 位移运算1.5.1 左移运算符 <<1.5.2 右移运算符 >>1.5.3 无符号右移运算符 >>> 二. 位运算实际运用2.1 判断奇偶性&#xff08;&…

DB9串口引脚介绍

一、公头和母头 图片示意源于网络: 二、 每个引脚的功能定义 公头&#xff1a;所有排针式的接头&#xff08;5针朝上&#xff0c;从左到右序号依次是1~9&#xff09; 母头&#xff1a;所有插槽式的接孔&#xff08;5孔朝上&#xff0c;从右到左序号依次是1~9&#xff09; 针…

计算机毕业设计选题推荐-掌心办公微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

Web与DNS综合实验

目录 题目&#xff1a; 步骤一&#xff1a;准备工作 步骤二&#xff1a;在server端搭建简单网页 步骤三&#xff1a;node1端的DNS解析配置 步骤五&#xff1a;node2端进行测试。 题目&#xff1a; 1.打开3个主机&#xff0c;server、node1、node2 2.server为web主机建立网…

typora使用PicGo自动上传图片到chevereto图床

typora使用PicGo自动上传图片到chevereto图床 近期发现&#xff0c;gitee图床不能用了。github又涉及科学上网。搜索了开源图床方案&#xff0c;找到了chevereto&#xff0c;使用起来还不错。分享给大家。 文章目录 typora使用PicGo自动上传图片到chevereto图床chevereto图床安…

轻量级 Java 日志组件

日志记录功能在开发中很常用&#xff0c;不仅可以记录程序运行的细节&#xff0c;方便调试&#xff0c;也可以记录用户的行为&#xff0c;是框架中不可或缺的组件。为最大程度复用现有的组件&#xff0c;我们就地取材使用了 JDK 自带的 JUL&#xff08;java.util.logging&#…

Linux--初识和几个简单的指令(1)

目录 前言 0.什么是操作系统 0.1 搭建 Linux 环境 0.2搭建 Linux 环境小结 1.使用 XShell 远程登录 Linux 1.1关于 Linux 桌面 1.2下载安装 XShell 1.3查看 Linux 主机 ip 1.4XShell 下的复制粘贴 2.Linux下基本指令 2.1 pwd命令 2.2 ls命令 2.3 mkdir指令 2.4 cd…

Linux上编译和安装SOFA23.06

前言 你可以直接使用编译安装好的SOFA版本Installing from all-included binaries (v23.06.00)&#xff1a; 如果你想自己编译&#xff0c;可以看我下面写的内容&#xff0c;不过绝大多数是从官网来的&#xff0c;如果和官网有出入&#xff0c;建议还是以官网为准。 在Linux下…

josef约瑟 时间继电器 ST3PA-A AC220V 带插座PF085A

ST3P系列时间继电器适用于交流50Hz或60Hz,额定电压380V及以下或直流24V的控制电路中作廷时元件,按预定的时间接通或分断电路。具有体积小,精度高,延时范围宽,可与JSZ3系列继电器等同互换使用。 系列型号 ST3PF-2Z(JSZ3F-2Z) 5s AC110V ST3PF(JSZ3F) 10s AC48V ST3PC-1(AH3-3)…

IJ中配置TortoiseSVN插件:

文章目录 一、报错情况&#xff1a;二、配置TortoiseSVN插件&#xff1a; 一、报错情况&#xff1a; 由于公司电脑加密&#xff0c;TortoiseSVN菜单没有提交和更新按钮&#xff0c;所以需要使用IJ的SVN进行代码相关操作 二、配置TortoiseSVN插件&#xff1a; 需要设置一个svn.…

Go 之 captcha 生成图像验证码

目前 chptcha 好像只可以生成纯数字的图像验证码&#xff0c;不过对于普通简单应用来说也足够了。captcha默认将store封装到内部&#xff0c;未提供对外操作的接口&#xff0c;因此使用自己显式生成的store&#xff0c;可以通过store自定义要生成的验证码。 package mainimpor…

LINUX入门篇【6】----第一个LINUX小程序---进度条及相关知识讲解

前言&#xff1a; 本篇我们将开始尝试构建我们的第一个LINUX的小程序----进度条作为一个十分常见的程序&#xff0c;在我们之后的工程实践中也是需要多次运用&#xff0c;但是介于我们目前还没有去学习网络等方面的知识&#xff0c;没法独立的去利用程序去下载一个真正的程序&…

【Proteus仿真】【Arduino单片机】LM35温度计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真Arduino单片机控制器&#xff0c;使用PCF8574、LCD1602液晶、LM35传感器等。 主要功能&#xff1a; 系统运行后&#xff0c;LCD1602显示传感器检测温度。 二、软件设计 /* 作者&a…

场景交互与场景漫游-路径漫游(7)

路径漫游 按照指定的路径进行漫游对一个演示是非常重要的。在osgViewer中&#xff0c;当第一次按下小写字母“z”时&#xff0c;开始记录动画路径;待动画录制完毕&#xff0c;按下大写字母“Z”&#xff0c;保存动画路径文件;使用osgViewer读取该动画路径文件时&#xff0c;会回…

Cadence virtuoso drc lvs pex 无法输入

问题描述&#xff1a;在PEX中的PEX options中 Ground node name 无法输入内容。 在save runset的时候也出现无法输入名称的情况 解决办法&#xff1a; copy一个.bashrc文件到自己的工作目录下 打开.bashrc文件 在.bashrc中加一行代码&#xff1a;unset XMODIFIERS 在终端sour…

java使用 TCP 的 Socket API 实现客户端服务器通信

一&#xff1a;什么是 Socket(套接字) Socket 套接字是由系统提供于网络通信的技术, 是基于 TCP/IP 协议的网络通信的基本操作&#xff0c;要进行网络通信, 需要有一个 socket 对象, 一个 socket 对象对应着一个 socket 文件, 这个文件在 网卡上而不是硬盘上, 所以有了 sokcet…

模块一、任务一.数据分析概述

一、module1 预测未来-总统大选 样本偏差 二、module2 优化现状-化妆品销售 1、数据分析师从业务类型上划分 2、目标&#xff1a;总销量 达到 目标销量 3、固定基本流程 &#xff08;1&#xff09;确定 一、目标值节节升高&#xff0c;是否合理&#xff1f;根据什么定的&…

【火炬之光-魔灵装备】

文章目录 装备天赋追忆石板技能魂烛刷图策略 装备 头部胸甲手套鞋子武器盾牌项链戒指腰带神格备注盾牌其余的装备要么是召唤物生命&#xff0c;要么是技能等级&#xff0c;鞋子的闪电技能等级加2不是核心&#xff0c;腰带的话主要是要冷却有冷却暗影的技能是不会断的&#xff…