半同步半异步进程池的实现

进程池的基本思想

由主进程管理所有监听socket,而各个子进程分别管理属于自己的连接的socket,子进程可以自己调用accept来接受新连接,这样父进程就无需向子进程传递socket,而只需要简单的说一声."我检测到了有新的连接,你来接受一下“

进程池的代码逻辑

  • 每个进程类都有一个m_pid用来标识自己,还有一个管道成员来统一信号源,处理信号事件
  • 进程池类使用单例模式创建,其构造函数为private,该类任何时刻只能有一个实例对象。通过create函数创建对象
  • 进程池类的构造函数中创建好process_number个子进程,将每个每个子进程的pid赋给子进程对象的成员m_pid,同时为每个进程创建一根与父进程通信的双向管道
    • 双向管道的一端既能读也能写,但如果从pipe[0]写入,只能从pipe[1]读出
  • 进程池类中的m_idx成员用来标识每个子进程在进程池中的序号,父进程的m_idx为-1;
  • run函数通过m_idx判断接下来是执行子进程的代码还是父进程的代码
  • 所有进程通过m_stop这个bool值来决定是否退出
  • run_parent()的代码逻辑
    • 通过setup_sig_pipe创建同一事件源的信号管道
    • 父进程有自己的epoll事件循环,每当有新的连接到来时,就采用Round Robin算法将新连接分配给一个子进程处理,被分配地子进程必须是“活着地”,通过其m_pid成员是否不等于 -1判断,父进程通过m_pipefd管道成员来通知子进程接受新连接
    • 信号通过sig_pipefd管道传给父进程,父进程使用signals数组存储收到的信号,并逐个处理信号,当收到SIGCHLD信号时,表示有子进程退出,使用waitpid无阻塞地回收子进程,遍历子进程数组,通过对比pid和每个子进程地m_pid成员,找到退出地子进程,将其m_pid设为-1,标识该进程已退出
    • 遍历子进程数组,查看是否所有子进程地m_pid都为-1,如果是,则父进程也退出(m_stop = true);
    • 当收到SIGTERM和SIGINT信号时,杀死所有子进程
  • run_child()代码逻辑
    • 通过setup_sig_pipe创建统一事件源的信号管道
    • 每个子进程也有自己的epoll事件循环,当m_pipefd管道上有可读事件发生时,表示有新的连接到来,处理这个新的socket连接,并将它得读写事件注册到自己得epoll内核事件表
    • 处理信号事件的逻辑和父进程类似但更简单多
    • 处理客户请求,调用逻辑处理对象的process办法处理之

代码实现

#ifndef PROCESSPOOL_H_
#define PROCESSPOOL_H_

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/wait.h>
#include <sys/stat.h>
//子进程的类
class process
{
public:
    process():m_pid(-1){}

public:
    pid_t m_pid;
    int m_pipefd[2];

};
//进程池类
template <typename T>
class processpool
{
private:
    //将构造函数设为私有,只能通过create来创建实例
    processpool(int listenfd,int process_number = 8);
public:
    //单例模式
    static processpool<T>* create(int listenfd,int process_number = 8)
    {
        if(!m_instance)
        {
            m_instance = new processpool<T>(listenfd,process_number);
        }
        return m_instance;
    }

    ~processpool()
    {
        delete [] m_sub_process;
    }
    //启动进程池
    void run();
private:
    void setup_sig_pipe();
    void run_parent();
    void run_child();
private:
    //进程池允许的最大子进程数量
    static const int MAX_PROCESS_NUMBER = 16;
    //每个子进程最多能处理的客户数量
    static const int USER_PER_PROCESS = 65536;
    //epoll最多能处理的事件数
    static const int MAX_EVENT_NUMBER = 10000;
    //进程总数
    int m_process_number;
    int m_epollfd;
    int m_listenfd;
    //子进程在池中的序号
    int m_idx;
    //子进程通过m_stop决定是否停止运行
    int m_stop;
    //保存所有子进程的描述信息
    process* m_sub_process;
    static processpool<T>* m_instance;
};

template <typename T>
processpool<T>* processpool<T>::m_instance = NULL;
//用于处理信号的管道,统一事件源
static int sig_pipefd[2];
static int setnonblocking(int fd)
{
    int old_flag = fcntl(fd,F_GETFL);
    int new_flag = old_flag | O_NONBLOCK;
    fcntl(fd,F_SETFL,new_flag);
    return old_flag;
}
static void addfd(int epollfd,int fd)
{
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN|EPOLLET;
    epoll_ctl(
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值