当前位置: 首页 > news >正文

进程池的实现与匿名管道通信(task 2)

目录

前言

核心逻辑

先描述

任务执行

初始化

控制子进程

资源退出

设置任务

缺陷

​编辑


前言

上文介绍了task1:什么是进程间的通信,什么是管道,如何利用匿名管道进行父子进程的通信。

本文将实现进程池的实现与匿名管道的通信。

核心逻辑


int main()
{LoadTask(&tasks);    srand(time(nullptr)^getpid()^1023); // 种一个随机数种子//1.获得子进程与管道vector<channel> channels;InitProPool(&channels);//2.创建任务,写入数据CtrlSlaver(channels);//3.资源清理QuitProcess(channels);return 0;
}

核心代码为:

1.创建子进程与管道

2.创建任务写入数据

3.资源清理

先描述

对于父子间通信的管道,需要用一个结构去描述管道的属性,以便后续通信


//描述管道属性
class channel
{
public:channel(int cmdfd, pid_t slaverid, string processname):_cmdfd(cmdfd),_slaverid(slaverid),_processname(processname){}public:int _cmdfd;     // 发送任务的文件描述符(后续write发送任务的时候只需要channel对象即可,read已经设置了从0号文件读)pid_t _slaverid;     // 子进程IDstring _processname;     // 子进程名称
};      //结构与类后面需要加分好:;

任务执行


void slaver()
{while (true){int cmdcode = 0;int ret = read(0, &cmdcode, sizeof(cmdcode));   //ssize_t read(int fd, void *buf, size_t count);if (ret == 4){std::cout <<"slaver say@ get a command: "<< getpid() << " : cmdcode: " <<  cmdcode << std::endl;if(cmdcode >= 0 && cmdcode < tasks.size()) tasks[cmdcode]();    //tasks内部数据是函数指针}else if (ret == 0)break;  //跳出循环}
}

初始化


// 输入:const &
// 输出:*
// 输入输出:&void InitProPool(vector<channel>* channels)
{// version 2: 确保每一个子进程都只有一个写端std::vector<int> oldfds;for (int i = 0; i < tasks_num; i++){//创建管道int pipefd[2] = {0};int ret = pipe(pipefd);if (ret < 0) return;//创建子进程pid_t id = fork();/*当父进程调用fork()时,以下步骤会发生:操作系统创建一个新的进程,这个新进程称为子进程。子进程获得父进程的代码段、数据段、堆、栈等资源的副本(注意是副本,但是这些资源在物理内存中是共享的,直到任一进程尝试写入时发生写时复制)。fork()在父进程中返回子进程的进程ID,在子进程中返回0。也就是说子进程会获得副本中的数据,同时执行fork下面的if(ret == 0)内部的代码。*/if (id == 0)     //子进程{std::cout << "child: " << getpid() << " close history fd: ";for(auto fd : oldfds) {          //oldfds存放的是写端,子文件需要关闭!所有的!写端std::cout << fd << " ";close(fd);}std::cout << "\n";close(pipefd[1]);dup2(pipefd[0], 0);  // 0:标准输入close(pipefd[0]);slaver();std::cout << "process : " << getpid() << " quit" << std::endl;exit(0);}//父进程oldfds.push_back(pipefd[1]);    //父进程开辟出写端之后,将写端push_backclose(pipefd[0]);std::string name = "process-" + std::to_string(i);channels->push_back(channel(pipefd[1], id, name));  //匿名对象传参  //一定要在父进程去初始化channel对象,因为channel对象需要子进程的pidsleep(1);   }
}

本阶段着重完成:1.创建管道 2.创建子进程 3.子进程调用读接口,在read处阻塞等待4.子进程创建结束之后初始化管道

控制子进程

void Menu()
{std::cout << "################################################" << std::endl;std::cout << "# 1. 刷新日志             2. 刷新出来野怪        #" << std::endl;std::cout << "# 3. 检测软件是否更新      4. 更新用的血量和蓝量  #" << std::endl;std::cout << "#                         0. 退出               #" << std::endl;std::cout << "#################################################" << std::endl;
}void CtrlSlaver(const std::vector<channel> &channels)
{int which = 0;while (true){//选择任务int select = 0; Menu();std::cout << "Please Enter@ ";std::cin >> select;if(select <= 0 || select >= 5) break;int cmdcode = select - 1;//选择进程:1.轮询 2.随机    // int processpos = rand()%channels.size();std::cout << "father say: " << " cmdcode: " <<cmdcode << " already sendto " << channels[which]._slaverid << " process name: " << channels[which]._processname<< std::endl;// 3. 发送任务write(channels[which]._cmdfd, &cmdcode, sizeof(cmdcode));   //一旦写入之后,被阻塞的子进程尝试启动  //每一个子进程都一个写端的wfdwhich++;which %= channels.size();}
}

核心逻辑:1.选择任务 2.选择子进程 3.发送任务

资源退出


void QuitProcess(const std::vector<channel> &channels)
{for(const auto &c : channels){     //close与wait一起进行。但是在子进程中,需要close(oldWfds)close(c._cmdfd);waitpid(c._slaverid, nullptr, 0);}// version1 // int last = channels.size()-1;// for(int i = last; i >= 0; i--)       //倒着进行// {//     close(channels[i]._cmdfd);//     waitpid(channels[i]._slaverid, nullptr, 0);// }// for(const auto &c : channels) close(c._cmdfd);   //先close再wait// // sleep(5);// for(const auto &c : channels) waitpid(c._slaverid, nullptr, 0);// // sleep(5);
}

设置任务

using namespace std;typedef void(*task_t)();void task1()
{std::cout << "lol 刷新日志" << std::endl;
}
void task2()
{std::cout << "lol 更新野区,刷新出来野怪" << std::endl;
}
void task3()
{std::cout << "lol 检测软件是否更新,如果需要,就提示用户" << std::endl;
}
void task4()
{std::cout << "lol 用户释放技能,更新用的血量和蓝量" << std::endl;
}void LoadTask(std::vector<task_t> *tasks)
{tasks->push_back(task1);    //函数名就是函数指针tasks->push_back(task2);tasks->push_back(task3);tasks->push_back(task4);
}

定义一个函数指针类型。用vector存储函数,用下表就可以访问函数。

缺陷

后续fork的子进程也会继承父亲的多个w接口,因此我们需要将子进程的w端彻底关闭。 

资源清理时的解决方案:关闭写端接口,读端读取到0,退出。

1.先关闭接口再清理。

2.倒着清理

最后一个子进程只被一个写端接口(来自父进程)指向,关闭之后,读端就没有了写端对应,可以实现退出。

如果正向进行,当来自父进程的写端关闭之后,还有后续其他子进程的写端指向该进程,因此无法关闭该进程。其他子进程的文件描述符表继承自父进程,因此还有其他子进程的写端指入该子进程的管道。

这个管道可以理解为文件缓冲区。

3.在Init阶段,就将父进程不断打开的写端fd记录到数组中

在子进程中,将写端fd彻底关闭。


http://www.mrgr.cn/news/68424.html

相关文章:

  • 【客观理性深入讨论国产中间件及数据库-科创基础软件】
  • sdm845(oneplus6)的启动漰溃ramdump被提交3e7f37解析-内核启动日志报错(空指针、Oops)
  • 鸿蒙生态未来的发展趋势
  • FastDDS服务发现之PDP的收发
  • Flutter鸿蒙next 实现长按录音按钮及动画特效
  • 当事人请求以审计单位的审计意见作为确定工程造价依据的,如何处理?
  • 论 ONLYOFFICE:开源办公套件的深度探索
  • 快手,抖音IP属地怎么更改?快手抖音更改IP属地教程
  • 使用Ollama在本地安装、编写Python代码和建立对话
  • 改变 van-tabs 默认选中颜色以及下划线颜色
  • 机械制造工控自动化监控界面:功能与美观兼具
  • 前端页面性能优化的常见问题与解决方案
  • 外包干了2年,快要废了。。。
  • 【数据价值化】数据资产入表:选择无形资产还是存货路线?
  • PyQt5 超详细入门级教程上篇
  • 【harbor】离线安装2.9.0-arm64架构服务制作和升级部署
  • 网页静态模板(html,css,js)
  • 毕业后如何查找获取文献
  • 一个非常有趣的挑战——物联网与AI结合的超级项目
  • Codeforces round72 div.2(经典二分)
  • 小型的网站服务器该如何选择配置?
  • 国内AI官网
  • 在日本IT行业工作加班多么?其实并不是这样!
  • 第4篇 滑动开关控制LED__ARM汇编语言工程<二>
  • Day39 | 动态规划 :完全背包应用 零钱兑换零钱兑换II
  • docker安装zookeeper,以及zk可视化界面介绍