• 进程池分析


    一般我们是通过动态创建子进程(或者子线程)来实现并发服务器的,这样的缺点

    (1)动态创建进程(或线程)比较耗费时间,这将导致较慢的客户响应

    (2)动态创建的子进程通常只用来为一个客户服务,这样导致了系统上产生大量的细微进程(或线程)。进程和线程间的切换将消耗大量CPU时间

    (3)动态创建的子进程是当前进程的完整映像,当前进程必须谨慎的管理其分配的文件描述符和堆内存等系统资源,否则子进程可能复制这些资源,从而使系统的可用资源急剧下降,进而影响服务器的性能。

    进程池:有服务器预先创建的一组子进程,这些子进程的数目在3~10个之间,

    进程池中的子进程:

    (1)他们都运行着相同的代码,具有相同的属性,比如优先级,PGID(组识别码)等。

    (2)进程池在服务器启动之初就创建好了,所以每个子进程都相对"干净",即它们没有打开不必要的文件描述符(从父进程继承而来)

    (3)也不会错误地使用大块的堆内存(从父进程复制得到)

    选择子进程为新任务服务的方式:

    (1)主进程使用某种算法来主动选择子进程

    (2)主进程和所有子进程通过一个共享的工作队列来实现同步

    :子进程都睡眠在该工作队列上,当有新的任务到来时,主进程将任务添加到工作队列中。

    这将唤醒正在等待任务的子进程,不过只有一个子进程将获得新任务的“接管权”,它可以从工作队列中取出任务并执行之,而其他子进程将继续睡眠在工作队列上。

    需求:

    主进程除了选择好子进程以外,还需要使用某种通知机制来告诉目标子进程有新任务需要处理,并传递必要的数据。

    最简单的办法:在父子进程之间预先建立好一条管道,然后通过该管道来实现所有的进程间通信(预先定义好协议来规范管道的使用)

    (父子线程间就可以直接用全局变量)

    处理多客户:

    问题1:

    监听socket和连接socket是否都由主进程来统一管理?

    半同步/半反应堆模式是由主进程统一管理这两种socket的

    高效的半同步/半异步模式以及领导者/追随者模式则是由主进程管理所有监听socket,而各个子进程分别管理属于自己的连接socket的。

    对于情况(1),主进程接受新的连接以得到连接socket,然后它需要将该socket传递给子进程(对于线程池而言,父线程将socket)传递给子线程是很简单的,因为他们可以很容易的共享该socket而父进程文件描述符的传递就要靠之前学习的知识来实现了)

    对于情况(2),子进程自己调用accept来接受新的连接,这样父进程就无需向子进程传递socket,而只需要简单的通知一声"我检测到新的连接,你来接受它。"

    半同步/半异步并发模式的进程池

    我们将接受新连接的操作放到子进程中,很显然,对于这种模式而言,一个客户连接上的所有任务始终是由一个子进程来处理的。

    我看的进程池是用C++实现的,我先说框架,后面我会努力改成C的

    类1:子进程类

    存放数据:

    目标子进程PID

    通道m_pipe

    类2:进程池类

    存放数据:

    (1)进程池允许的最大子进程数

    (2)每个子进程最多能处理客户数量

    (3)epoll最多能处理的事件数

    (4)进程池中的进程总数

    (5)子进程在池中的序号,从0开始

    (6)每个进程都有一个epoll内核事件表,用m_epollfd标识

    (7)监听socket

    (8)标识是否停止运行

    (9)进程指针保存子进程的描述信息

    (10)进程池静态实力

    函数:

    (1)初始化,创建一个线程池

    (2)销毁线程池

    (3)统一事件源

    (4)启动父进程

    (5)启动子进程

    (6)run启动进程池

    3:由于用了epoll所以需要epoll的一系列函数

    4:信号处理函数

    5:信号添加函数

    问题一:如何存放子进程

    首先,父子进程之间是一定要通信的,而子进程都有自己的pid那么

    可以设置一个结构体

    struct process

    {

      pid_t pid;

      int pipefd[2];

    }

    通过这个结构体构造一个数组,然后里面放的就是各个进程,通过进程的pid来找到相应子进程来调用子进程的通道进行通信.

    问题二:关于进程池如何唤醒自己相应的子进程

    使用的是epoll监听事件,首先确定自己要监听子进程的什么事件然后再注册事件

    使用run_child函数去跑第i个子进程,然后再主函数中通过for(epoll_wait返回的是活跃客户端的个数)对活跃的事件进行一一处理

    用进程池存放好子进程的pid和管道,有新的链接来的时候就使用池子里的子进程去完成操作

  • 相关阅读:
    性能分析与调优思想
    python散列实现映射抽象数据类型
    python接口模拟100个用户登录
    大O记法
    linux查看操作系统版本信息
    Python招聘信息
    flask-login模块官网内容整理
    python|base|环境搭建
    echarts|map
    mysql|unsigned 与 signed 类型
  • 原文地址:https://www.cnblogs.com/cthon/p/9069703.html
Copyright © 2020-2023  润新知