【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.cc
        g++ -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);//没必要往文件中写入\0
     cout<<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://www.mfbz.cn/a/567603.html

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

相关文章

制氢机远程监控运维方案

制氢机远程监控运维方案 在当今能源转型的大背景下&#xff0c;氢能作为清洁、高效且可再生的能源载体&#xff0c;其重要性日益凸显。而制氢机作为氢能产业链中的关键设备&#xff0c;其稳定运行与高效运维对于保障氢气供应、推动氢能产业健康发展至关重要。在此背景下&#…

动态规划——切割钢条问题

一、动态规划 动态规划算法通常用于解决最优化问题&#xff08;寻求最优解&#xff09;。其思想与分治法类似&#xff0c;将待求解的问题分成若干个子问题&#xff0c;先求出子问题&#xff0c;再根据子问题的解求出原来问题中的解&#xff0c;与分支法不同的是&#xff0c;在动…

Oracle使用内部包自定义创建表空间和用户

如果之前有类似的表空间,可以使用dbms自动生成对应的表空间和数据文件 select dbms_metadata.get_ddl(TABLESPACE,ts.tablespace_name) from dba_tablespaces ts; 可以使用类似的 SQL> set echo off SQL> spool /data/logs/create_tablespace.log SQL> select dbms…

Mimics21软件学习总结

一. Mimics21软件安装过程 ① 解压下载好的Mimics软件包&#xff1b; ② 双击“MIS_Medical_21.0.exe”打开等待安装程序初始化完成&#xff1b; ③ 进入安装向导点击“next”&#xff1b; ④ 点击选择“Iaccept the agreement”同意相关协议&#xff0c;随后点击“next”&…

多模态大模型训练数据以及微调数据格式

多模态数据&#xff0c;尤其是中文多模态数据&#xff0c;找一些中文多模态的数据 中文多模态数据集汇总_数据集-阿里云天池本文整理汇总了业界常用的多模态中文数据集&#xff0c;提供了每个数据集的简介、官网、下载地址、Github代码等信息&#xff0c;方便算法研究人员学习…

虚假新闻检测——Adapting Fake News Detection to the Era of Large Language Models

论文地址&#xff1a;https://arxiv.org/abs/2311.04917 1.概论 尽管大量的研究致力于虚假新闻检测&#xff0c;这些研究普遍存在两大局限性&#xff1a;其一&#xff0c;它们往往默认所有新闻文本均出自人类之手&#xff0c;忽略了机器深度改写乃至生成的真实新闻日益增长的现…

Etsy多账号关联怎么办?Etsy店铺防关联解决方法

Etsy虽然相对于其他跨境电商平台来说比较小众&#xff0c;但因为平台是以卖手工艺品为主的&#xff0c;所以成本较低&#xff0c;利润很高。许多跨境卖家都纷纷入驻&#xff0c;导致平台规则越发严格&#xff0c;操作不当就会封号&#xff0c;比如一个卖家操作多个账号会出现关…

国外问卷调查如何做?需要借助海外住宅IP吗?

在数字化时代&#xff0c;国外问卷调查不仅是了解市场需求的重要手段&#xff0c;还成为了一项能够赚取额外收入的方式。随着全球范围内消费者行为的多样化&#xff0c;各类企业和机构越来越需要了解不同地区的用户观点和偏好&#xff0c;以优化产品和服务。 一、国外问卷调查…

Flask中的JWT认证构建安全的用户身份验证系统

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Flask中的JWT认证&#xff1a;构建安全的用户身份验证系统 随着Web应用程序的发展&#xf…

Linux给磁盘扩容(LVM方式)

Linux给磁盘扩容&#xff08;LVM方式&#xff09; 最近测试性能&#xff0c;在本地打数据时&#xff0c;发现磁盘空间不足&#xff0c;于是想手动给/挂载点添加空间。这里介绍通过LVM方式快速给磁盘扩容。 LVM:是一种技术&#xff0c;方便管理磁盘。如果不用LVM&#xff0c;那…

js的算法-交换排序(快速排序)

快速排序 基本思想 快速排序的基本思想是基于分治法的&#xff1a;在待排序表L【1...n】中任意取一个元素p 作为枢轴&#xff08;或基准&#xff0c;通常取首元素&#xff09;。通过一趟排序将待排序表划分为独立的两部分L【1...k-1】和L【k1...n】;这样的话&#xff0c;L【1…

Linux下的基本指令

基本指令 前言ls 指令语法功能常用选项举例注意关于拼接关于 -a关于文件ls与/的联用ls与根目录ls与任意文件夹ls与常用选项与路径 pwd命令语法功能常用选项注意window与Linux文件路径的区别 cd 指令语法功能举例注意cd路径... touch指令语法功能常用选项 mkdir指令语法功能常用…

【RAG 论文】Query2doc — 使用 LLM 做 Query Expansion 来提高信息检索能力

论文&#xff1a;Query2doc: Query Expansion with Large Language Models ⭐⭐⭐⭐⭐ Microsoft Research, EMNLP 2023 文章目录 背景介绍Query2doc 论文速读实现细节实验结果和分析总结分析 背景介绍 信息检索&#xff08;Information Retrieval&#xff0c;IR&#xff09;指…

如何操作HTTP返回头-ApiHug小技巧-002

&#x1f917; ApiHug {Postman|Swagger|Api...} 快↑ 准√ 省↓ GitHub - apihug/apihug.com: All abou the Apihug apihug.com: 有爱&#xff0c;有温度&#xff0c;有质量&#xff0c;有信任ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace &…

如何用微信小程序实现远程控制无人售货柜

如何用微信小程序实现远程控制无人售货柜呢&#xff1f; 本文描述了使用微信小程序调用HTTP接口&#xff0c;实现控制无人售货柜&#xff0c;独立控制售货柜、格子柜的柜门。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 序号设备名称厂商1智能WiFi…

【Canvas与艺术】绘制金色八卦图

【关键点】 等比例缩放各部件及将八卦转为“二进制”的过程。 【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>使用…

gcc make makefile cmake之间的关系梳理

gcc是GNU Compiler Collection&#xff08;GNU编译器套件&#xff09;&#xff0c;也可以简单认为是编译器&#xff0c;它可以编译很多编程语言&#xff08;包括C、C、Object-C、Fortran、Java等&#xff09;当你的程序只有一个源文件&#xff0c;直接用gcc命令编译它。但是当你…

【Java--数据结构】提升你的编程段位:泛型入门指南,一看就会!

前言 泛型是一种编程概念&#xff0c;它允许我们编写可以适用于多种数据类型的代码。通过使用泛型&#xff0c;我们可以在编译时期将具体的数据类型作为参数传递给代码&#xff0c;从而实现代码的复用和灵活性。 在传统的编程中&#xff0c;我们通常需要为不同的数据类型编写不…

总结一下背包里的顺序和是否逆序

1.对于01背包而言&#xff0c;一维压缩态只能物品到背包且需要逆序 2.对应多重背包而言&#xff0c;组合数物品到背包&#xff0c;排列数背包到物品&#xff0c;且都需要正序

【北京迅为】《iTOP-3588开发板系统编程手册》-第20章 socket 应用编程

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…
最新文章