信息手机网站模板,做网站推广哪些,网页策划方案模板范文,淘宝客需要自己做网站吗目录 一、进程间通信的理解 
1.为什么进程间要通信 
2.如何进行通信 
二、匿名管道 
1.管道的理解 
2.匿名管道的使用 
3.管道的五种特性 
4.管道的四种通信情况 
5.管道缓冲区容量 
三、进程池 
1.进程池的理解 
2.进程池的制作 
四、源码  
1.ProcessPool.hpp 
2.Task.hpp 
3…目录 一、进程间通信的理解 
1.为什么进程间要通信 
2.如何进行通信 
二、匿名管道 
1.管道的理解 
2.匿名管道的使用 
3.管道的五种特性 
4.管道的四种通信情况 
5.管道缓冲区容量 
三、进程池 
1.进程池的理解 
2.进程池的制作 
四、源码  
1.ProcessPool.hpp 
2.Task.hpp 
3.test.cc 本文旨在分享我对管道技术的理解和见解。由于个人知识和经验的限制文中可能存在错误或遗漏。我真诚地邀请各位读者在阅读过程中发现任何问题时指出错误并提供宝贵的意见。您的反馈将是我不断进步和完善的重要动力。 一、进程间通信的理解 
1.为什么进程间要通信 首先进程之间是相互独立的尽管是父子进程之间它们虽然资源共享但当子进程需要修改数据时仍然需要进行写时拷贝保持独立性。 而让进程间通信可以实现数据之间的交互资源共享事件通知又或者是让一个进程对另一个进程进行控制。 进程间通信是操作系统中实现进程间协作和数据交换的重要机制它使得多个进程能够共同完成任务提高系统的效率和可靠性。 
2.如何进行通信 进程间通信的原理其实很简单只需要两个进程共同访问一个资源而一个进程对资源的更改能被另一进程感知到从而做出相应的操作。 所以通信的前提是进程之间能够访问同一个资源而且该资源是公共的而不是某进程内部的。 
二、匿名管道 
1.管道的理解 我们把进程之间通信的介质资源叫作管道。开发者在设计管道技术时文件系统已经比较成熟所以为了方便管理该资源就使用文件来实现而对文件的读写就是通信的过程但它与一般的文件还是有些区别文件都是储存到磁盘上的而进程之间通信用的文件并不需要把它储存到磁盘上它只是作为一个传输介质。 它比较特殊所以起名为管道。管道其实是一个内存级的文件。 注意父子进程之间的管道叫作匿名管道顾名思义就是没有名字也不需要名字因为子进程能够继承下来父进程开辟的管道资源。  
2.匿名管道的使用 
创建匿名管道常用的接口是 int pipe(int pipefd[2]); 
需要包含头文件 #includeunistd.h 
返回值创建成功返回0失败返回-1参数这个是一个输出型参数传入一个int类型长度为2的数组然后得到pipefd[0]以读的方式打开的文件描述符pipefd[1]以写的方式打开的文件描述符。 
示例 
#include iostream
#include sys/types.h
#include unistd.h
int main()
{int pipefd[2];pipe(pipefd);int rfd  pipefd[0],wfd  pipefd[1];pid_t id  fork();if(id  0){close(wfd);//关闭子进程的写文件只让它读int k0;while(true){read(rfd,k,sizeof(k));printf(read:%d\n,k);}}else{close(rfd);//关闭父进程的读文件只让它写。int num0;while(true){write(wfd,num,sizeof(num));num;sleep(1);}}return 0;
} 因为只是一个小测试代码写的并不严谨没有检查调用是否成功没有关闭文件没有进程等待大家不用太在意能说明问题就行。 要记住pipefd[2]中哪个是读哪个是写有一个小技巧0像嘴巴所以下标为0的是读1像钢笔所以1下标是写。 
3.管道的五种特性 
匿名管道只能用来进行具有血缘关系的进程间通信用于父与子。管道文件自带同步机制。如上代码示例父进程写一次休眠一秒而子进程是一直不断地读快的一端会迁就于慢的一端最后实现同步。管道是面向字节流的。怎么读与怎么写并没有联系比如写入“hello world”但可能读到“hel”这取决于你要读多少字节。管道是单向通信的。也就是a表示进程写的时候b读。b写的时候a在读。而不是既在写同时也在读。管道文件的生命周期是随进程的。进程结束管道也随之销毁。 
4.管道的四种通信情况 
写慢读快 --- 读端就要阻塞等待写端写入。写快读慢 --- 到管道容量满了后写端就要阻塞等待读端读取数据然后就可以覆盖式地继续往管道写入。写关闭读继续 --- read就会返回0表示文件结尾。写继续读关闭 --- 写端不再有意义系统会杀掉写端进程。 
5.管道缓冲区容量 管道缓冲区容量为64kb大家可以根据管道的性质与通信特点自行进行测试我这里就不再展示。 
三、进程池 
1.进程池的理解 在程序使用内存的时候比如vector扩容机制会提前给你开辟一块空间供你使用尽管现在用不到相当于做一下预备。减少开辟空间的频次从而达到提高效率的效果。 那么进程池也同样给父进程提前开辟一些子进程提供父进程使用。其中我们使用匿名管道建立联系。 在父进程给子进程派发任务时为了提高效率会让每个子进程均匀地分配到任务称为负载均匀而不是把大部分的任务都派发到一个子进程上通常会有以下策略 
轮询按顺序一一分配。随机随机进行分配。负载因子设计一个负载因子让子进程按负载因子的大小排成一个小根堆每次取出堆头的子进程派发任务然后更新负载因子插回到堆中。 
2.进程池的制作 在面向对象的编程中最重要的就是对对象的描述与组织这里我们的核心就是对管道进行管理。那么首先需要一个类对管道进行描述。 
class Channel
{
public:Channel(int wfd, int id) : _wfd(wfd), _id(id){}//... ...~Channel(){}
private:int _wfd;int _id;
}; 
_wfd是该管道对应写端的fd_id是该管道对应的子进程的pid。  这里我们不必把rfd读端fd加入因为我们现在对管道的描述组织目的是方便父进程管理而rfd是给子进程用的所以不用添加为变量。 这里我们就以轮询的方式派发任务刚才创建的Channel相当于对管道的描述接下来创建ChannelManage进行组织。这里选择使用数组来管理派发任务方式选择轮询所以需要记录下一个需要派发到的管道的下标。 
class ChannelManage
{
public:ChannelManage():_next(0){}//... ...~ChannelManage(){}
private:vectorChannel _channels;int _next;
}; 
接下来还需要创建一个类对整体的进程池做管理。 
class ProcessPool
{
public:ProcessPool(int num) : _n(num){}// ... ...~ProcessPool(){}
private:ChannelManage _cm;int _n;
};其中_n表示需要创建多少子进程这是由使用者来决定的。 
在ProcessPool中我们准备实现这些方法 void Create()用于创建子进程。         由于我们是要生成多个通道所以需要循环来进行而单趟循环需要做以下这些操作         1.创建管道然后创建子进程。这样能让子进程继承到管道信息         2.关于子进程写端关闭然后执行Work()最后把读端关闭并exit退出。         3.关于父进程读端关闭然后把wfdpid存入_cm中。 void Work(int  rfd)用于子进程读取任务码并执行命令。void Run()用于获取并派发任务。void Stop()用于关闭写端并回收子进程。 最后为方便测试我们还需要一个管理任务的类和方法。我们可以单独创建一个Task.hpp文件。 
class TaskManage
{
public:TaskManage(){   //随机数种子srand((unsigned int)time(nullptr));}int GetCode(){   //随机生成任务码数组下标return rand()%_tasks.size();}void ExeTask(int code){   //执行任务_tasks[code]();}// ... ...~TaskManage(){}
private:vectorfunctionvoid() _tasks;//用于储存任务的数组
}; 然后需要在ProcessPool中放入TaskManage成员变量并在ProcessPool的构造函数中完成对_tasks中内容的插入。具体操作参考下面源码。 
四、源码  
1.ProcessPool.hpp 
#pragma once
#include iostream
#include unistd.h
#include cstdio
#include sys/wait.h
#include vector
#include Task.hpp
#define NUM 5
using namespace std;
class Channel
{
public:Channel(int wfd, int id) : _wfd(wfd), _id(id){}void Write(int code){write(_wfd,code,sizeof(code));}void Close(){close(_wfd);}void WaitPid(){waitpid(_id,nullptr,0);cout等待进程_id成功endl;}~Channel(){}private:int _wfd;int _id;
};
class ChannelManage
{
public:ChannelManage():_next(0){}void Insert(int wfd, int id){_channels.emplace_back(wfd, id);}int GetChannel()//轮询访问子进程{int tmp  _next;_next;_next % _channels.size();return tmp;}void WriteManage(int code,int index){_channels[index].Write(code);}void Close(){for(int i0;i_channels.size();i){_channels[i].Close();}}void WaitPid(){for(int i0;i_channels.size();i){_channels[i].WaitPid();}}~ChannelManage(){}
private:vectorChannel _channels;int _next;
};class ProcessPool
{
public:ProcessPool(int num) : _n(num){_tm.InsertTask(PrintLog);_tm.InsertTask(Download);_tm.InsertTask(Upload);}void Work(int rfd){while (true){int code;ssize_t n  read(rfd, code, sizeof(code));if (n  0){if(n ! sizeof(code)) continue;else{//执行任务_tm.ExeTask(code);}}else{cout子进程getpid()退出endl;break;}}}void Create(){for (int i  0; i  _n; i){int pipefd[2];pipe(pipefd);pid_t id  fork();if (id  0){close(pipefd[1]);Work(pipefd[0]);close(pipefd[0]);exit(0);}else{close(pipefd[0]);_cm.Insert(pipefd[1], id);}}}void Run(){int ChannelCode  _cm.GetChannel();int TaskCode  _tm.GetCode();_cm.WriteManage(TaskCode,ChannelCode);}void Stop(){_cm.Close();_cm.WaitPid();}~ProcessPool(){}
private:ChannelManage _cm;int _n;TaskManage _tm;
};2.Task.hpp 
#pragma once
#include iostream
#include vector
#include functional
#include ctime
#include cstdlib
using namespace std;
void PrintLog()
{std::cout  我是一个打印日志的任务  std::endl;
}void Download()
{std::cout  我是一个下载的任务  std::endl;
}void Upload()
{std::cout  我是一个上传的任务  std::endl;
}
class TaskManage
{
public:TaskManage(){srand((unsigned int)time(nullptr));}void InsertTask(void(*fun)()){_tasks.push_back(fun);}int GetCode(){return rand()%_tasks.size();}void ExeTask(int code){_tasks[code]();}~TaskManage(){}
private:vectorfunctionvoid() _tasks;
}; 
3.test.cc 
#include ProcessPool.hpp
int main()
{ProcessPool pp(NUM);pp.Create();int k  10;while(k--){pp.Run();}pp.Stop();return 0;
}