【Linux系统编程十九】:(进程通信)--匿名管道/模拟实现进程池
【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 5std::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;//子进程的pidstd::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.清理收尾}
相关文章:

【Linux系统编程十九】:(进程通信)--匿名管道/模拟实现进程池
【Linux系统编程十九】:匿名管道原理/模拟实现进程池 一.进程通信理解二.通信实现原理三.系统接口四.五大特性与四种情况五.应用场景--进程池 一.进程通信理解 什么是通信? 通信其实就是一个进程想把数据给另一个进程,但因为进程具有独立性…...

【全网首发】2023年NOIP真题
目录 前言 真题 结尾 前言 NOIP题目了解一下,后续有可能会出讲解,题目全部来自于洛谷 真题 第一题:词典 第二题:三值逻辑 第三题:双序列扩展 第四题: 天天爱打卡 结尾 大家可以把你的预期分数打在评论…...

【Linux网络】从原理到实操,感受PXE无人值守自动化高效批量网络安装系统
一、PXE网络批量装机的介绍 1、常见的三种系统安装方式 2、回顾系统安装的过程,了解系统安装的必要条件 3、什么是pxe 4、搭建pxe的原理 5、Linux的光盘镜像中的isolinux中的相关文件学习 二、关于实现PXE无人值守装机的四大文件与五个软件的对应关系详解 5个…...

Pandas+Matplotlib 数据分析
利用可视化探索图表 一、数据可视化与探索图 数据可视化是指用图形或表格的方式来呈现数据。图表能够清楚地呈现数据性质, 以及数据间或属性间的关系,可以轻易地让人看图释义。用户通过探索图(Exploratory Graph)可以了解数据的…...

k8s ingress高级用法一
前面的文章中,我们讲述了ingress的基础应用,接下来继续讲解ingress的一些高级用法 一、ingress限流 在实际的生产环境中,有时间我们需要对服务进行限流,避免单位时间内访问次数过多,常用的一些限流的参数如下&#x…...

C语言--从键盘输入10个数字放在数组中,并输出
用scanf读取数字的时候要注意,可以输入一个数字,按一下回车,输入一个数字,按一下回车,也可以一次性输入完10个数据。(中间可以用空格隔开,系统会自动识别) 输出一:每按下一个数字&am…...

SSL加密
小王学习录 今日摘录前言HTTP + SSL = HTTPSSSL加密1. 对称加密2. 非对称加密 + 对称加密3. 证书今日摘录 但愿四海无尘沙,有人卖酒仍卖花。 前言 SSL表示安全套接层,是一个用于保护计算机网络中数据传输安全的协议。SSL通过加密来防止第三方恶意截取并篡改数据。在实际应用…...

一个美观且功能丰富的 .NET 控制台应用程序开源库
推荐一个美观且功能丰富的 .NET 控制台应用程序开源库,从此告别黑漆漆的界面。 01 项目简介 Spectre.Console 是一个开源的 .NET 库,用于创建美观、功能丰富的控制台(命令行)应用程序。它提供了一组易于使用的 API,…...
DispatcherSynchronizationContext and Dispatcher
https://www.cnblogs.com/liangouyang/archive/2008/11/20/1337907.html SynchronizationContext提供一个自由线程的同步上下文,一个常用的用法是把UI线程的同步上下文保存起来,传给另一个线程,因为UI只能再UI线程中操作,在另外一…...
java类型属性set方法无法被赋值
前言 遇到一个基础的问题 方法设置属性值 失败 问题代码 有个内部类的User对象 分别使用 方式一和 方式二 设置User的属性值 发现方式一的属性并不能被设置成功 可以自行测试下 public class Test{public static void main(String[] args) {#方式一 User user new User();u…...

【2】SM2验签工具和RSA验签工具
0X01 前言 最近看了好多验签工具,感觉不是很好用,就自己造了个。 0x02 工具功能介绍 对SM2算法进行验签和RSA算分进行验签,签名值可以是base64,也可以是十六进制。 兼容各种输入。 0x03 工具使用 RSA 验签 SM2 验签 0x04 工具…...

Python (十一) 迭代器与生成器
迭代器 迭代器是访问集合元素的一种方式,可以记住遍历的位置的对象 迭代器有两个基本的方法:iter() 和 next() 字符串,列表或元组对象都可用于创建迭代器 字符串迭代 str1 Python str_iter iter(str1) print(next(str_iter)) print(next(st…...

通过maven命令手动上传jar私服Nexus
Nexus3在界面上传组件时报: Ext.JSON.decode(): Youre trying to decode an invalid JSON String: 查找了很多资料,都没有解决。有哪位大佬知道的评论告诉一下,万分感谢。 于是换成maven命令上传: mvn deploy:deploy-file -Dgr…...

记一次用jlink调试正常,不进入调试就不能运行的情况
一、概述 我开机会闪烁所有指示灯,但是重新上电时,指示灯并没有闪烁,就像"卡死"了一样。 使用jlink的swd接口进行调试,需要多点几次运行才能跳转到main函数里面。 调试模式第一次点击运行,暂停查看函数堆栈…...
搞科研、写论文,如何正确使用GPT?AIGC技术解析、提示词工程高级技巧、AI绘图、ChatGPT/GPT4应用
目录 专题一 OpenAI开发者大会最新技术发展及最新功能应用 专题二 AIGC技术解析 专题三 提示词工程高级技巧 专题四 ChatGPT/GPT4的实用案例 专题五 让ChatGPT/GPT4成为你的论文助手 专题六 让ChatGPT/GPT4成为你的编程助手 专题七 让ChatGPT/GPT4进行数据处理 专题八 …...
Java实现的插件化策略模式
Java实现的插件化策略模式 目录结构实现BaseDealAnno.java(注解)BasePluginEnum.java(枚举)BaseDealFactory.javaContextBaseDealListener.java(核心类)BaseDealHandler.java(接口)BaseAudioService.java(可扩展多个)验证目录结构 com.demo.mytest ├── strategy │ ├──…...
【jvm】MinorGC、MajorGC和FullGC
目录 一、说明二、年轻代GC(Minor GC)触发机制三、老年代GC (Major GC/Full GC)触发机制四、Full GC触发机制 一、说明 1.jvm调优的主要目的是GC的时间少一些,用户线程执行的时间多一些 2.主要侧重于MajorGC和FullGC,这两块的GC产生的暂停时间比MinorGC产…...
Redis:java和SpringBoot中使用Redis
目录 Jedis操作Redis6SpringBoot整合Redis Jedis操作Redis6 Jedis是java开发的操作redis的工具包。 引入maven依赖: <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>4.2.1</ve…...

Java的XWPFTemplate word生成列表
Java的XWPFTemplate工具类导出word.docx的使用_xwpftemplate 语法_youmdt的博客-CSDN博客 如果是表格的列表参考上面这篇文章即可,比较复杂的列表遍历暂时还没找到方法,只能手动创建表格了 上面是模板,非常简单,以为我们是要自己创…...
基于RFID的自动化仓储设备研发项目可行性研究报告
一、项目概况 基于RFID的自动化仓储设备研发项目,是深圳市飞鸟国际供应链股份有限公司在公司有色金属供应链一站式服务的基础上,针对有色金属供应链的仓储环节进行的研发。 多年来,公司一直坚持以“科技金融”为核心的未来发展战略…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...

深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...