资讯动态

Linux之进程间通信(二)---模拟进程池

发布时间:2026/8/6 16:31:53 来源:尧图企业网站定制
进程池创建多个子进程只有一个父进程一个子进程外层套循环创建多个进程父进程还要对channle进行先描述在组织对管道进行描述-class Channel大纲1.创建多个子进程2.对子进程和管道进行管理3.释放资源阶段检测是否创建子进程成功和文件描述符接下来对现有函数进行封装--利用c类class ProcessPool { public: // a. 创建多个进程,创建多个管道 int CreateProcessPool() { for (int i 0; i 5; i) { int pipefd[2] {0}; int n pipe(pipefd); if (n 0) { cerr pipe error endl; return pipeerror; } pid_t id fork(); if (id 0) { cerr fork error endl; return forkerror; } else if (id 0) { // 子进程 close(pipefd[1]); // 子进程关掉写,进行读 DoTask(pipefd[0]); exit(OK); } else { // 父进程 close(pipefd[0]); // 父进程写 // pipefd[1]循环一下就没有了,要储存 Channel ch(pipefd[1], id); // 将子进程和要写的管道文件进行记录 channels.push_back(ch); cout 创建子进程成功,pid: id endl; } } return OK; } void DebugPrint() { for(auto ch : channels) { ch.Print(); } } private: // 未来维护和组织所有的channel vectorChannel channels; };将创建多个子进程,和打印,文件描述符,创建子进程是否成功都封装到ProcessPool这个类里面,然后将vectorChannel channels 变成该类的成员变量,main函数的内容变得十分简洁实现完成不同任务的需求约定统一四字节进行读写任务我们想要不要让他很重复,这个时候要用到函数指针,然后回调的操作来实现functionvoid(int)可调用对象包装器。void函数返回值为 void(int)这个可调用对象接收 1 个 int 类型参数task_t就是别名代表所有可以接收 int 参数、返回 void 的可调用类型。可以包装普通函数、函数指针、lambda 表达式、仿函数对象。using task_tfunctionvoid(int);task_t是类型别名代表一个可调用对象接收 1 个 int 参数返回 voidvoid CreateProcessPool(task_t cb)cbcallback 回调类型是task_t。意思函数CreateProcessPool接收一个可调用对象进来不在本函数内部执行交给子进程去调用。//子进程分支 close(pipefd[1]); cb(pipefd[0]); // 执行回调 exit(OK);子进程拿到管道读端pipefd[0]把文件描述符当做实参传给回调执行用户传入的函数。pool.Init(DoTask);把DoTask函数传进去。DoTask签名void DoTask(int fd)正好匹配void(int)。整体流程main 把 DoTask 作为回调传给 Init → Init 传给 CreateProcessPool → fork 出子进程 → 子进程调用cb(pipefd[0])等价执行DoTask(pipefd[0])。对子进程和管道进行管理那么要如何选择一个进程呢???比较好的是将任务平均分配到子进程1.轮询2.随机3.权重接下来我们采用轮询的方式来分配任务选择任务制作任务清单任务表实现的底层typedef 干了什么typedef void(*mytask_list)();void (*)()这是函数指针类型代表保存一个函数地址该函数没有参数返回值 void。typedef给这个复杂类型起一个简短名字mytask_list。// 普通int a; a是int类型变量 // 现在mytask_list fp; fp就是一个函数指针变量可以存函数地址 mytask_list fp SyncDick; fp(); //等价 SyncDick();mytask_list mytask[4];mytask[4]这是函数指针数组数组里面每一格都能存放一个函数的地址。{SyncDick, Download, PrintLog, UpdateStatus}把 4 个全局函数的地址依次填进数组 0、1、2、3 下标位置。数组下标存储的函数地址调用写法0SyncDickmytask[0]();→执行 SyncDick ()1Downloadmytask[1]();→执行 Download ()2PrintLogmytask[2]();→执行 PrintLog ()3UpdateStatusmytask[3]();→执行 UpdateStatus ()种下随机数种子^pid,让取出来的数更加随机验证任务分配是否随机发送任务给进程processpoolChannel补充写和管道名的成员函数子进程完成发送过来的任务释放资源processpool创建成员函数Quit用来释放资源要将管道关闭然后将子进程进行回收问题一如果父进程关闭,但是子进程还没有跑完会有影响吗是没有的,子进程只有将管道内容全部读完才会结束问题二将Quit这样可以吗发现卡住了那么这是为什么呢???实际上是因为管道文件没有被关干净子进程是以父进程为模板创建的,很多的内核数据结构的信息都是和父进程一样的也就是说,越到后面创建的子进程对于前面父进程创造的管道文件都拥有写的权限将父进程对于管道文件的写权限关闭,其管道文件对应的子进程并不能接受到管道以全部关闭,然后再退出,因为这其中后面创建的子进程对于改管道文件依然可以写入那么应该要如何解决这个问题呢???解决方案:1.从最后一个创建的子进程开始关闭管道文件2.实现真正w:r1:1让之后创建的子进程关闭之前的管道文件

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价