1. 进程池
在匿名管道学习后,我们已经实现了一个基于匿名管道的简单进程控制,现在我们学习了命名管道来试试用命名管道来实现进程控制吧。 正在匿名管道时,我们已经实现了进程的控制,命名管道只要在其基础上进行一些修改就可以了。
2. 进程池的功能2.1 可被执行的任务进程池是为了实现对进程的控制,通过父进程对众多子进程的管理,实现高效的进程执行任务。 我们可以先虚构一些任务出来,后续的子进程就来执行这些任务:
打印日志将数据插入数据库请求网络 可以用这些任务可以写成单独的函数,然后用一个类来管理这些任务。 类的成员变量: 利用哈希表来存储这些任务 类的功能有:展示可执行的任务执行任务 具体的代码如下:代码语言:javascript代码运行次数:0运行复制typedef void(*task_t)(void);/*函数指针*///任务集void printLog(){ std::cout<<"PID:"< st_;public: Task() { st_ = {{"打印日志",printLog},{"插入数据",MySQLInsert},{"请求网络",NetRequest}}; } void showTask()/*展示任务*/ { std::cout<<"可选任务:"<2.2 进程控制(重点)进程控制是进程池的重点、核心。我们实现进程池也就是为了执行对进程的控制。 如何控制进程呢?
创建进程展示空闲子进程子进程等待任务的发配父进程为子进程发配任务杀死/回收进程2.2.1 子进程类为了实现这些功能,同样我们用一个类来封装它们。除此之外,还记得我们在实现基于匿名管道的进程控制吗?我们还需要一个类来描述子进程。因为我们需要知道父进程的每个写端的文件描述符和哪个子进程通过命名管道建立起来联系。 所以子进程类具有的属性就有了:
子进程的PID写端的文件描述符编号名字 其中第3和第4个属性是为了方便父进程管理而加入的,后续进程控制类需要用数组将各个子进程存储起来。 子进程类代码如下:代码语言:javascript代码运行次数:0运行复制class SubProcesses{private: pid_t id_; int processNum_; int wfd_; std::string name_; static int cnt_;public: SubProcesses(pid_t id,int wfd) :id_(id),processNum_(cnt_++),wfd_(wfd) { char buff[SIZE]{0}; snprintf(buff,SIZE,"Process %d | pid:wfd [%d:%d]",processNum_,id_,wfd_); name_ = buff; } pid_t getId() { return id_; } std::string getName() { return name_; } int getWfd() { return wfd_; }};int SubProcesses::cnt_ = 1;2.2 进程控制那么进程类会具有什么属性呢?我们需要将创建出子进程进行管理可以用一个
vecotr
来对其进行管理,还需要知道我们需要创建的子进程数量,因为是基于命名管道的进程控制,为此我们还必须对个个进程管道进行管理,同样用vector
将它们装起来。除此之外在来个权限掩码也可以。 如此进程控制类的成员变量:管理着子进程的vector
类。管理着命名管道的vector
类。记录需要创建的子进程数目。权限掩码。 下面开始实现函数:2.2.2 创建子进程创建子进程,创建子进程同样还是用到
fork
函数,不同的是不在需要pipe
函数来创建管道了。现在已经变成了mkfifo
函数。 和匿名管道那一样,我们要记得关闭子进程的写端,尽管子进程并没有主动打开写端,但是因为子进程继承父进程的缘故,依然会吧父进程的写端给继承过了的。一点定是要关闭的。代码语言:javascript代码运行次数:0运行复制void createProcess(){std::vector vfd;/*关闭子进程的写端*/for(int i = 1;i<=subProcessNum_;++i){std::string fifoName = "fifo-"+std::to_string(i); int ret = mkfifo(fifoName.c_str(),mode_);/*创建命名管道*/if(ret == -1){perror("mkfifo");exit(1);}namePipe_.push_back(fifoName);/*存入命名管道*/pid_t id = fork();/*创建子进程*/if(id < 0){perror("fork");exit(1);}else if(id == 0){//子进程for(auto&fd:vfd){close(fd);}int rfd = open(fifoName.c_str(),O_RDONLY);if(rfd == -1){perror("open");exit(1);}//开始等待父进程的指令waitTask(rfd); /*子进程等待任务委派*/close(rfd);exit(0);}//父进程int wfd = open(fifoName.c_str(),O_WRONLY);if(wfd == -1){perror("open");exit(1);}subProcess_.push_back({id,wfd});/*存入子进程信息*/fifoName.push_back(wfd);}} 2.2.3 子进程等待任务死循环等待任务,当写端关闭再退出。
代码语言:javascript代码运行次数:0运行复制void waitTask(int rfd)/*子进程等待任务委派*/{while(true){char buff[SIZE];int n = read(rfd,buff,SIZE);if(n == -1){perror("read");exit(1);}else if(n>0){Task().curTask((std::string)buff);}else if(n == 0){/*证明写端没有写消息了*/std::cout<<"写端已经关闭,读端也即将关闭!"<2.2.4 展示空闲进程代码语言:javascript代码运行次数:0运行复制/*展示可选进程*/ void showProcess() { std::cout<<"目前可用进程有:"<2.2.5 分配任务给子进程该函数的功能就分配任务,由用户自己选择子进程来执行任务。
代码语言:javascript代码运行次数:0运行复制/*下达任务给子进程*/void sendTask(){std::cout<<"------------------------"<>input; }while(input<0||input>=subProcessNum_);Task().showTask();std::string taskName;std::cout<<"请选择任务#";std::cin>>taskName;if(taskName == "exit"){break;}std::cout<<"选择进程-> "<2.2.6 回收子进程回收子进程的同时也不能忘记关闭文件描述符和关闭命名管道
代码语言:javascript代码运行次数:0运行复制/*关闭写端、删除文件、等待子进程退出*/ void waitProcess() { for(int i = 0;i代码代码语言:javascript代码运行次数:0运行复制#include #include #include #include #include #include #include #include #include #include










