Linux操作系统小项目——实现《进程池》

news/2024/10/15 11:44:28/

文章目录

  • 前言:
  • 代码实现:
  • 原理讲解:
    • 细节处理:

前言:

在前面的学习中,我们简单的了解了下进程之间的通信方式,目前我们只能知道父子进程的通信是通过匿名管道的方式进行通信的,这是因为这点我们就可以简简单单的来写一个项目————进程

代码实现:

Task.hpp文件

#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/wait.h>class Channel
{
public:Channel(int wfd, int child_process_id, std::string child_process_name): _wfd(wfd), _child_process_id(child_process_id), _child_process_name(child_process_name){}~Channel(){}int Get_wfd() { return _wfd;}int Get_child_process_id() { return _child_process_id;}std::string Get_child_process_name() { return _child_process_name;}void CloseChannel() { close(_wfd); }void Wait(){pid_t rid = waitpid(_child_process_id, nullptr, 0);if (rid < 0){std::cerr << "Filed to wait" << std::endl;exit(1);}std::cout << "Wait success!" << std::endl;}private:int _wfd;int _child_process_id;std::string _child_process_name;
};void task_print()
{std::cout << "Hey! This is task_print" << std::endl;
}void task_load()
{std::cout << "Yo! This is task_load" << std::endl;
}void task_flush()
{std::cout << "Dude! This is task_flush" << std::endl;
}typedef void (*task_ptr)(void); // function pointertask_ptr task_arr[3]; // function pointer's arrayvoid TaskLoad()
{srand((unsigned int)time(NULL));task_arr[0] = task_print;task_arr[1] = task_load;task_arr[2] = task_flush;std::cout << "Task are all be loaded!" << std::endl;std::cout << "------------------------" << std::endl;
}int ChoseTask()
{int n = rand() % 3;return n;
}int PipeNumber(int times)
{static int _pipe = 0;int next = _pipe;_pipe++;_pipe %= times;return next;
}void WriteToPipe(int p_number, int t_number, std::vector<Channel> Channels)
{static int i = 1;int n = write(Channels[p_number].Get_wfd(), &t_number, sizeof(t_number));if(n < 0) {std::cerr << "failed to write" << std::endl;exit(1);}std::cout << i++ << "->the number of task '" << t_number << "' have writen in pipe ' " << p_number << "' already" << std::endl;
}void work(int rfd)
{while(true){sleep(1);int file_buffer = 0;int n = read(rfd, &file_buffer, sizeof(file_buffer));if(n < 0){std::cerr << "Failed to read" << std::endl;exit(1);}else if(n == 0){std::cout << "child process : " << getpid() << " quit" << std::endl;break;}task_arr[file_buffer]();}
}

processpool.cc文件

#include "Task.hpp"void CreateChannels(int nums, std::vector<Channel> *Channels)
{for (int i = 0; i < nums; ++i){// create pipeint pipefd[2] = {0};int n = pipe(pipefd);if (n < 0){std::cerr << "Failed to create pipe" << std::endl;exit(1);}// create child processint pid = fork();if (pid == 0){// childif (!Channels->empty()){std::cout << "----Before I close my pipefd[1], I have to close the wfd which inherited by the father process's struct files_struct!" << std::endl;for (auto &channel : *Channels)channel.CloseChannel();}std::cout << "Here is child process!" << std::endl;close(pipefd[1]); // as child process close unnecessary wfdwork(pipefd[0]);close(pipefd[0]);exit(0);}// fatherstd::string name = "Channel No.";std::string Number = std::to_string(i);name += i;close(pipefd[0]); // as father process close unnecessary rfdChannels->push_back({pipefd[1], pid, name});}
}void ControlChannels(int times, std::vector<Channel> Channels)
{for (int i = 0; i < times; ++i){sleep(1);// chose pipe number to be writen in pipeint pipe_number = PipeNumber(times);// chose task number to write in pipeint task_number = ChoseTask();// write task number to the pointed pipeWriteToPipe(pipe_number, task_number, Channels);}
}void CleanChannels(std::vector<Channel> Channels)
{for (auto &channel : Channels){channel.CloseChannel();// channel.Wait();}// for (auto &channel : Channels)// {//     channel.Wait();// }
}int main(int argc, char *argv[])
{if (argc == 1){std::cerr << "Need more argc! need more option!" << std::endl;return 1;}int nums = std::stoi(argv[1]);std::vector<Channel> Channels;// load all the functionTaskLoad();// 1、create channels and child processCreateChannels(nums, &Channels);// 2、control channelsControlChannels(nums, Channels);// 3、free channels and wait child process exitCleanChannels(Channels);// sleep(100);return 0;
}

原理讲解:

其实这个进程池项目通过画图就很好能做出来:
image-20241010102548398

细节处理:

  1. 由图片可知,未来可能我们要实现很多个任务的时候完全可以采用进程池的方式来解决,与之前处理任务不同的是,以前我们是要执行一个任务就创建一个子进程,而这个不一样,现在是提前就已经开辟好了一堆子进程
    所以先提前创建好适量的子进程,有任务就交给子进程执行,这么做更适合多任务的并发操作,因为节省了创建子进程的操作。

  2. 未来master可以将任务发送至管道内部,以此来唤醒子进程来处理任务,但是要注意父进程不可针对的使用同一个管道,应当在多个管道直接轮询写入,这就保证了后端任务划分的负载均衡

  3. 具体的实现将任务发送至管道内部,从代码的角度来看其实就是将函数发送到管道之中,所以我们可以先创建一个函数指针数组来进行管理各个函数,未来在将任务写入至管道也仅仅需要传递下标(数字)即可。

  4. 在回收的时候我们也要额外注意!我们可以先让父进程遍历所有管道并关闭所有管道wfd(这主要依据管道的5种特征——“若是管道wfd关闭了,且rfd还没关闭,那么迟早会读到文件末尾,那么read系统调用就会返回0,就会结束读取”),再遍历一遍,等待每一个子进程退出。等待的原因其实是为了能够在子进程执行完后进行回收,从而避免出现“孤儿进程”。

  5. (BUG!!!!!)切记!不可以一边关闭管道wfd再等待子进程退出,这会造成子进程阻塞!!!
    image-20241010104818857

    image-20241010105101099


http://www.ppmy.cn/news/1539415.html

相关文章

Go-知识依赖GOPATH

Go-知识依赖GOPATH 1. 介绍2. GOROOT 是什么3. GOPATH 是什么4. 依赖查找5. GOPATH 的缺点 1. 介绍 早期Go语言单纯地使用GOPATH管理依赖&#xff0c;但是GOPATH不方便管理依赖的多个版本&#xff0c;后来增加了vendor&#xff0c;允许把项目依赖 连同项目源码一同管理。Go 1.…

PL/SQL Developer如何连接Oracle数据库(汉化)

简介 PL/SQL Developer是一种用于Oracle数据库开发的集成开发环境&#xff08;IDE&#xff09;。它提供了一个可视化的界面&#xff0c;使开发人员能够方便地编写、调试和执行PL/SQL代码。PL/SQL Developer还具有其他功能&#xff0c;如数据库对象浏览器、SQL编辑器、数据导入…

P2056 [ZJOI2007] 捉迷藏 题解

P2056 [ZJOI2007] 捉迷藏 题面&#xff1a; 题目描述 Jiajia 和 Wind 是一对恩爱的夫妻&#xff0c;并且他们有很多孩子。某天&#xff0c;Jiajia、Wind 和孩子们决定在家里玩捉迷藏游戏。他们的家很大且构造很奇特&#xff0c;由 N N N 个屋子和 N − 1 N-1 N−1 条双向走廊…

【Vue】Vue3.0(十一)Vue 3.0 中 computed 计算属性概念、使用及示例

上篇文章&#xff1a;【Vue】Vue3.0&#xff08;十&#xff09;toRefs()和toRef()的区别及使用示例 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Vue专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年10月15日10点23分 文章目录 Vue 3.0中…

19 Shell Script awk命令

Shell Script awk命令 一、awk 一&#xff09;awk介绍 ​ awk是一个强大的文本分析工具&#xff0c;相对于grep的查找&#xff0c;sed的编辑&#xff0c;awk在其对数据分析并生成报告时&#xff0c;显得尤为强大。简单来说awk就是把文件逐行的读入&#xff0c;以空格为默认分…

docker方式k8s环境搭建及pod简介

目录 一 搭建环境准备 二 搭建步骤 三 pod简介 3.1 pod介绍 3.2 pod配置 3.3 pod生命周期 3.4 pod调度 一 搭建环境准备 1.1 关闭防火墙和selinux&#xff0c;有自己搭建好的harbor仓库 1.2 先禁用服务器的交换分区&#xff08;如果服务器内存不够&#xff0c;k8s使用交…

理解Token和Session:鉴权与会话管理的区别

理解Token和Session&#xff1a;鉴权与会话管理的区别 在Web应用和API设计中&#xff0c;鉴权与会话管理是两个核心概念&#xff0c;它们对于确保用户身份的安全性和维护用户会话状态至关重要。Token和Session是两种常用的鉴权与会话管理机制&#xff0c;它们各自具有独特的工…

揭秘网络流量分析系统:保障IT运维稳定的幕后英雄

AnaTraf 网络性能监控系统NPM | 全流量回溯分析 | 网络故障排除工具 在现代企业的IT运维中&#xff0c;保障网络的稳定运行和业务的连续性是重中之重。网络流量的监控和优化不仅能提升网络性能&#xff0c;还能帮助及时发现潜在故障&#xff0c;防止网络瘫痪。因此&#xff0c…