【linux】匿名管道|进程池

1.进程为什么要通信?
进程也是需要某种协同的,所以如何协同的前提条件(通信)
通信数据的类别:
1.通知就绪的
2.单纯的数据
3.控制相关的信息

2.进程如何通信?
进程间通信,成本会高一点
进程间通信的前提,先让不同的进程,看到同一份(资源,一段内存)
一定是某一个进程需要通信,让os创建一个共享资源
os必须提供很多的系统调用,os创建的共享资源不同,系统调用接口不同,进程间通信会用不同的种类

3.进程通信的常见方式是什么?
消息队列
共享内存
信号量
直接复用内核代码直接通信呢?
管道:
1.命名管道
2.匿名管道


1.管道(匿名管道)

我们之前在父子进程那里,我们说过父进程和子进程是相互独立的,但是子进程可以继承父进程的好多属性,把紫色的框的内容都拷贝一份给子进程,保证了struct_file_struct,中指针数组中存在的文件结构体地址是一样的
在这里插入图片描述

在这里插入图片描述
这样一来父进程和子进程就可以操控同一个内核级缓冲区了,如果子进程要发消息给父进程,因为只有一个内核级缓冲区提供使用,也是为了防止误写,我们把父进程的文件操作符下标为4的关闭,让父进程不能往内核级缓冲区内写入,(父进程的写入对应的文件操作符关闭,并不影响子进程的写入,存在内核级的引用计数,父进程关闭,引用计数-1),同时让子进程把文件操作符下标为3的关闭,子进程不能读内核缓冲区的数据。struct_file内存在系统调用函数的函数指针,方便操作底层,让子进程调用write(),写入对应的内核级缓冲区,父进程调用read(),读出子进程写入的数据,实现了父子进程间的通信。

1.为什么我们子进程主动close(0/1/2),不影响父进程继续使用显示器文件呢?
上面黄色的地方已解答
2.父子进程既然关闭不需要的fd(文件操作符),为什么要曾经打开呢?可以不关闭吗?
为了让子进程继承读写方式的操作,因为不知道此时子进程要读还是写,可以不关闭,建议关了,会出现误写操作。


上面实现父子进程通信出现的问题就是,写入内核级缓冲区的内容,操作系统会把他们刷新到磁盘文件中去log.txt,为了解决这个问题,提出一个函数pipe()

2.pipe()函数

头文件:#include <unistd.h>
作用:相当提供一个匿名的文件,提供一个数组,数组大小为2,保存该进程的匿名文件的文件操作符,向匿名文件的内核级缓冲区写入的时候,不会刷新到磁盘中去。
返回值:返回0,成功,返回-1,失败
类似于open函数
在这里插入图片描述
pipefd[0]保存读匿名文件的文件结构体的文件操作符

pipefd[1]保存写匿名文件的文件结构体的文件操作符


站在文件描述符角度-深度理解管道
在这里插入图片描述

为什么让管道单向通信?
简单->不会出现误写,也不用考虑在内核级缓冲区中是父进程写入子进程读或者相反
如果要双向通信?
两个管道可以解决,一个管道父进程给子进程写,一个管道子进程给父进程写


3.代码实现匿名管道实现父子进程通信

makefile

pipe:pipe.ccg++ -o pipe pipe.cc -std=c++11
.PHONY:clean
clean:rm -rf pipe

pipe.cc

#include<iostream>
#include<unistd.h>
#include<string.h>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
const int size=1024;
string getstring()
{
static int cnt=0;
string messageid=to_string(cnt);
cnt++;
pid_t id=getpid();
string stringpid=to_string(id);
string message="messageid: ";
message+=messageid;
message+="my pid is:";
message+=stringpid;
return  message;
}
void childwrite(int wfd)
{
string message="father ,i AM youchild";
while(true)
{
string info=message+getstring();
write(wfd,info.c_str(),info.size());
sleep(1);
}
}void fatherread(int rfd)
{
char inbuffer[size];
while(true){ssize_t n=read(rfd,inbuffer,sizeof(inbuffer)-1);//没必要往文件中写入\0cout<<inbuffer<<endl;}sleep(1);
}int main()
{
int pipefd[2];
int n=pipe(pipefd);
if(n!=0)
{
perror("pipe fail");
}
cout<<"pipefd[0]:"<<pipefd[0]<<"pipefd[1]:"<<pipefd[1]<<endl;
pid_t id=fork();
if(id==0)
{
cout<<"子进程开始发消息了"<<endl;
close(pipefd[0]);
childwrite(pipefd[1]);
close(pipefd[1]);
exit(0);
}
sleep(1);
close(pipefd[1]);
cout<<"父进程开始接受消息了"<<endl;
fatherread(pipefd[0]);
sleep(5);
close(pipefd[0]);}

代码解释:pipe函数给该进程分配两个文件操作符,由于默认进程打开标准输入流,标准输出流,标准错误流,占了文件操作符0,1,2,所以从3开始分配,pipe返回值不等于0表示创建匿名文件失败,等于0,创建成功,pipefd[0]放的是读该文件的文件操作符,pipefd[1]放的是写该文件的文件操作符。
fork之后创建子进程,子进程关闭自己的读的文件操作符,通过childwrite()函数给该匿名文件的内核级缓冲区写入,父进程在fatherread中读取该文件内核级缓冲区的内容,实现了父子进程通信
在这里插入图片描述


管道的4种情况
1.如果管道内部是空的&&write fd没有关闭,读取条件不具备,读进程会被阻塞,等到读取条件具备,写入管道数据以满足读取条件(子进程写入缓冲区的时间变长,父进程读取阻塞)
2.管道被写满&&read fd不读且没有关闭,管道被写满,写进程会被阻塞(写条件不具备)->wait–写条件具备<-读取数据来满足写条件具备(父进程sleep时间变长,一直不读,管道被写满)
3.管道一直读&&写端关闭wfd,读端read返回值为0,表示读到了文件结尾(子进程写一个字符,直接break)
4.rwd直接关闭,写端wfd一直在进行写入?写端进程会被操作系统直接使用13号信号关闭,相当于进程出现了异常
管道的5种特征
1.匿名管道:只用来进行具有血缘关系的进程之间,进行通信。常用与父子进程之间通信
2.管道内部,自带进程之间的同步机制(多执行流执行代码的时候,具有明显的顺序性)
3.管道文件的生命周期是随着进程的
4.管道文件在通信的时候,是面向字节流的.(write的次数和读的次数不一样)
5.管道的通信方式,是一种特殊的半双工模式


4.进程池

创建子进程和管道

在这里插入图片描述

#include<iostream>
#include<string>
#include<vector>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
class channel
{public:channel(int wfd,pid_t id,const string&name):_wfd(wfd),_subprocessid(id),_name(name){}~channel(){}int getwfd(){return _wfd;}pid_t getprocessid(){return _subprocessid;}string getname(){return _name;}private:int _wfd;pid_t _subprocessid;string _name;
};
int main(int argc,char *argv[])
{
if(argc!=2){cout<<"usage:"<<argv[0]<<"processnum"<<endl;}
int num=stoi(argv[1]);
vector<channel>channels;
for(int i=0;i<num;i++){int pipefd[2]={0};int n=pipe(pipefd);if(n<0)exit(1);pid_t id=fork();if(id==0){close(pipefd[0]);//task();close(pipefd[1]);exit(0);}string channel_name="channel-"+to_string(i);close(pipefd[0]);channels.push_back(channel(pipefd[1],id,channel_name));}for(auto channel:channels)//测试{cout<<"----------------------"<<endl;cout<<"wfd:"<<channel.getwfd()<<endl;cout<<"subprocessid:"<<channel.getprocessid()<<endl;cout<<"name:"<<channel.getname()<<endl;}}

在这里插入图片描述


选择子进程要执行的功能,选择管道(对应哪个子进程)

在这里插入图片描述
whichone保证了父进程对后端任务划分负载均衡。
task.hpp

#pragma once
#include<iostream>
#include<cstdlib>
#include<ctime>
#define Tasknum 3
typedef void(*task_t());
void run()
{
cout<<"实现角色的跑"<<endl;
}
void jump()
{cout<<"实现角色的跳"<<endl;}
void climb()
{cout<<"实现角色的爬"<<endl;}task_t tasks[Tasknum];
void loadtask()
{
srand(time(nullptr));
tasks[0]=run;
tasks[1]=jump;
tasks[2]=climb;
}
void  runtask(int number)
{
if(number<0||number>2)return ;
tasks[number]();
}
int slecttask()
{
return rand()%Tasknum;
}

process.cc

#include<iostream>
#include<string>
#include<vector>
#include <sys/types.h>
#include <unistd.h>
#include"task.hpp"
using namespace std;
class channel
{public:channel(int wfd,pid_t id,const string&name):_wfd(wfd),_subprocessid(id),_name(name){}~channel(){}int getwfd(){return _wfd;}pid_t getprocessid(){return _subprocessid;}string getname(){return _name;}private:int _wfd;pid_t _subprocessid;string _name;
};
int whichchannel(int channelnum)
{
static int count=0;
int channel=count;
count++;
count%=channelnum;
return channel;
}
int main(int argc,char *argv[])
{
//第一步:创建子进程和管道
if(argc!=2){cout<<"usage:"<<argv[0]<<"processnum"<<endl;}
int num=stoi(argv[1]);
vector<channel>channels;
for(int i=0;i<num;i++){int pipefd[2]={0};int n=pipe(pipefd);if(n<0)exit(1);pid_t id=fork();if(id==0){close(pipefd[0]);//task();close(pipefd[1]);exit(0);}string channel_name="channel-"+to_string(i);close(pipefd[0]);channels.push_back(channel(pipefd[1],id,channel_name));}//for(auto channel:channels)//{// cout<<"----------------------"<<endl;// cout<<"wfd:"<<channel.getwfd()<<endl;// cout<<"subprocessid:"<<channel.getprocessid()<<endl;// cout<<"name:"<<channel.getname()<<endl;//}loadtask();//第二步:选择任务int taskcommand=slecttask();
//第三步:选择哪一个子进程来执行对应任务int channel_id=whichchannel(num);}

发送函数指针数组下标

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/5803950461cc4ab695dc20d57680f1e8.png

task.hpp

#pragma once
#include<iostream>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef void(*task_t)();
void run()
{
cout<<"实现角色的跑"<<endl;
}
void jump()
{cout<<"实现角色的跳"<<endl;}
void climb()
{cout<<"实现角色的爬"<<endl;}task_t tasks[3];
void loadtask()
{
srand(time(nullptr));
tasks[0]=run;
tasks[1]=jump;
tasks[2]=climb;
}
void  runtask(int number)
{
if(number<0||number>2)return ;
tasks[number]();
}
int slecttask()
{
return rand()%3;
}

process.cc

#include<iostream>
#include<string>
#include<vector>
#include <sys/types.h>
#include <unistd.h>
#include"task.hpp"
#include <sys/wait.h>
using namespace std;
class channel
{public:channel(int wfd,pid_t id,const string&name):_wfd(wfd),_subprocessid(id),_name(name){}void closechannel(){close(_wfd);}void wait(){pid_t rid=waitpid(_subprocessid,nullptr,0);if(rid>0){cout<<"wait"<<rid<<"sussess"<<endl;}}~channel(){}int getwfd(){return _wfd;}pid_t getprocessid(){return _subprocessid;}string getname(){return _name;}private:int _wfd;pid_t _subprocessid;string _name;
};
int whichone(int channelnum)
{
static int count=0;
int channel=count;
count++;
count%=channelnum;
return channel;
}
void sendtasknum(channel& chan,int taskcommand)
{write(chan.getwfd(),&taskcommand,sizeof(taskcommand));
}void task(int index)
{
while(true){int command=0;int n=read(index,&command,sizeof(command));if(n==sizeof(int)){cout<<"pid is:"<<getpid()<<"handle task"<<endl;runtask(command);}  else if(n==0){cout<<"sub process:"<<getpid()<<"quit!!"<<endl;break;}}
}
void cleanchannels(vector<channel>& channels)
{
for(auto &channel:channels)
{
channel.closechannel();
}
for(auto &channel:channels)
{
channel.wait();}}int main(int argc,char *argv[])
{
//第一步:创建子进程和管道
if(argc!=2){cout<<"usage:"<<argv[0]<<"processnum"<<endl;}
int num=stoi(argv[1]);loadtask();
vector<channel>channels;
for(int i=0;i<num;i++){int pipefd[2]={0};int n=pipe(pipefd);if(n<0)exit(1);pid_t id=fork();if(id==0){close(pipefd[1]);task(pipefd[0]);//第五步close(pipefd[0]);exit(0);}string  channel_name="channel-"+to_string(i);close(pipefd[0]);channels.push_back(channel(pipefd[1],id,channel_name));}
while(true)
{	
sleep(1);
//第二步:选择任务int taskcommand=slecttask();
//第三步:选择哪一个子进程来执行对应任务int channel_id=whichone(num);
//第四步:向第三步选择的管道发送第二步选择的函数指针数组下标sendtasknum(channels[channel_id],taskcommand);
//第六步回收
}cleanchannels(channels);}

管道回收,以及子进程释放

在上一步已经回收了管道,以及子进程释放,回收管道,直接关闭对应管道的写端即可,子进程释放,让父进程使用waitpid即可,
下面来处理一种误区
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


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

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

相关文章

《html自用使用指南》--基于w3School实践

1.基础标签 文本输入时&#xff0c;在编辑器中的换行&#xff0c;多个空格&#xff0c;都被编辑器看作一个空格 <p> 这个段落 在源代码 中 包含 许多行 但是 浏览器 忽略了 它们。 </p>结果&#xff1a;这个段落 在源代码 中 包含 许多行 但是 浏览器…

java多线程-悲观锁、乐观锁

简介 悲观锁&#xff1a;没有安全感&#xff0c;一上来就直接加锁&#xff0c;每次只能一个线程进入访问&#xff0c;访问完毕之后&#xff0c;再解锁。线程安全&#xff0c;但是性能差。乐观锁&#xff1a;很乐观&#xff0c;一开始不上锁&#xff0c;认为没有问题。等到要出现…

AI预测福彩3D第9套算法实战化测试第1弹2024年4月23日第1次重新测试

上篇文章咱们开启了实战化测试&#xff0c;也就是将之前的推荐方案直接缩为6码定位&#xff0c;再配合缩号&#xff0c;争取缩至4-5码。由于昨天的第一次测试&#xff0c;AI模型的某个参数设置错误&#xff0c;导致结果跟预期的相差较大&#xff0c;咱们今天修正下参数重新开启…

16.Nacos环境隔离

环境隔离namespace Namespace->Group->Service/Data->集群->实例 Namespace: Group&#xff1a; nacos控制台新增一个开发环境的命名空间&#xff1a;dev, 会产生命名空间的id。 将命名空间的id配置到微服务的配置文件中&#xff1a; spring:cloud:nacos:server…

【C语言】手撕二叉树

标题&#xff1a;【C语言】手撕二叉树 水墨不写bug 正文开始&#xff1a; 二叉树是一种基本的树形数据结构&#xff0c;对于初学者学习树形结构而言较容易接受。二叉树作为一种数据结构&#xff0c;在单纯存储数据方面没有 顺序表&#xff0c;链表&#xff0c;队列等线性结构…

ZNS SSD+F2FS文件系统|如何降低GC开销?--2

在F2FS&#xff08;Flash-Friendly File System&#xff09;中&#xff0c;Over-provisioning&#xff0c;OP配置是一种优化策略&#xff0c;旨在通过预留一部分存储空间不分配给用户使用&#xff0c;以提升文件系统的性能、耐用性和可靠性。在F2FS与ZNS SSD的结合中&#xff0…

Win10 打开有些软件主界面会白屏不显示,其他软件都正常

环境&#xff1a; Win10专业版 英伟达4070 显卡 问题描述&#xff1a; Win10 打开有些软件主界面会白屏不显示,打开远程协助软件AIRMdesk,白色&#xff0c;其他软件都正常 解决方案&#xff1a; 网上说电脑没有接显示器独立显卡的关系导致 我是只有一台主机&#xff0c;没…

mmclassification 训练自己的数据集

文章目录 从源码安装数据集准备config文件训练附录 从源码安装 git clone https://github.com/open-mmlab/mmpretrain.git cd mmpretrain pip install -U openmim && mim install -e .下面是我使用的版本 /media/xp/data/pydoc/mmlab/mmpretrain$ pip show mmcv mmpr…

Fisher判别示例:鸢尾花(iris)数据(R)

先读取iris数据&#xff0c;再用程序包MASS&#xff08;记得要在使用MASS前下载好该程序包&#xff09;中的线性函数lda()作判别分析&#xff1a; data(iris) #读入数据 iris #展示数据 attach(iris) #用变量名绑定对应数据 library(MASS) #加载MASS程序包 ldlda(Species~…

路由引入,过滤实验

实验拓补图 实验目的&#xff1a; 1、按照图示配置 IP 地址&#xff0c;R1&#xff0c;R3&#xff0c;R4 loopback口模拟业务网段 2、R1 和 R2 运行 RIPv2,R2&#xff0c;R3和R4运行 OSPF&#xff0c;各自协议内部互通 3、在 RIP 和 oSPF 间配置双向路由引入,要求除 R4 上的…

Jackson 2.x 系列【30】Spring Boot 集成之数据脱敏

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 本系列Spring Boot 版本 3.2.4 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 1. 概述2. 实现思路3. 案例演示3.1 脱敏规则3.2 自…

【全网首发】Mogdb 5.0.6新特性:CM双网卡生产落地方案

在写这篇文章的时候&#xff0c;刚刚加班结束&#xff0c;顺手写了这篇文章。 前言 某大型全国性行业核心系统数据库需要A、B两个物理隔离的双网卡架构方案&#xff0c;已成为行业标准。而最新发布的MogDB 5.0.6的CM新增支持流复制双网段部署&#xff0c;用于网卡级高可用容灾(…

Vue--》深入了解 VueUse 功能性工具集

今天博主为大家介绍一款实用性的插件名字叫做 VueUse &#xff0c;它是专门为 Vue.js 生态系统设计的功能性工具集合。其提供了许多可重用的功能函数&#xff0c;可以帮助开发者更轻松地构建 Vue.js 应用程序。其提供了大量的功能&#xff0c;包括状态管理、副作用管理、组合式…

SpringCloud系列(12)--服务提供者(Service Provider)集群搭建

前言&#xff1a;在上一章节中我们成功把微服务注册进了Eureka集群&#xff0c;但这还不够&#xff0c;虽然注册服务中心Eureka已经是服务配置了&#xff0c;但服务提供者目前只有一个&#xff0c;如果服务提供者宕机了或者流量过大&#xff0c;都会影响到用户即服务使用者的使…

GoJudge环境部署本地调用云服务器部署go-judge判题机详细部署教程go-judge多语言支持

前言 本文基于go-judge项目搭建&#xff0c;由于go-judge官网项目GitHub - criyle/go-judge: Sandbox Server in REST / gRPC API. Based on Linux container technologies.&#xff0c;资料太少&#xff0c;而且只给了C语言的调用样例&#xff0c;无法知道其他常见语言比如&am…

4.23学习总结

一.NIO(一) (一).简介: NIO 是 Java SE 1.4 引入的一组新的 I/O 相关的 API&#xff0c;它提供了非阻塞式 I/O、选择器、通道、缓冲区等新的概念和机制。相比与传统的 I/O 多出的 N 不是单纯的 New&#xff0c;更多的是代表了 Non-blocking 非阻塞&#xff0c;NIO具有更高的并…

OKCC搭建配置什么样的服务器合适

OKCC呼叫中心系统是一种采用软硬件结合的架构方式、及分布式的IP技术&#xff0c;从多角度为企业提供整合的一体化解决方案。因此&#xff0c;搭建OKCC呼叫中心系统所使用的服务器应该满足以下几点要求&#xff1a; 稳定性&#xff1a;服务器需要具有较高的稳定性和可靠性&…

JavaSE——程序逻辑控制

1. 顺序结构 顺序结构 比较简单&#xff0c;按照代码书写的顺序一行一行执行。 例如&#xff1a; public static void main(String[] args) {System.out.println(111);System.out.println(222);System.out.println(333);} 运行结果如下&#xff1a; 如果调整代码的书写顺序 , …

2024华中杯A题|太阳能路灯光伏板的朝向设计问题(思路、代码.....)

太阳能路灯由太阳能电池板组件部分(包括支架)、LED灯头、控制箱(包含控制器、蓄电池)、市电辅助器和灯杆几部分构成。太阳能电池板通过支架固定在灯杆上端。太阳能电池板也叫光伏板,它利用光伏效应接收太阳辐射能并转化为电能输出,经过充放电控制器储存在蓄电池中。太阳能…

Midjourney-01 初试上手 注册使用并生成你的第一张AI图片 详细流程 提示词 过程截图 生成结果 付费文生图的天花板!

背景介绍 Midjourney是一款基于人工智能技术的绘画软件&#xff0c;利用深度学习算法来辅助用户进行绘画创作。这款软件能够通过用户输入的文本描述生成图像&#xff0c;支持多种生成方式&#xff0c;包括文字生成图片、图片生成图片和混合图片生成图片。 图像生成方式&#…