• apache2 进程/线程模型


    apache2 有两种进程:主进程( master_main)和子进程( child_main )。

    子进程又派生了三种线程:

    1、control thread  控制线程。是主线程。负责创建 accept thread, worker threads ,接收和执行 master  process 的控制指令。
     * events.

    2、listener threads  负责处理监听
    3、worker threads  负责处理数据交互

     // c:\httpd-2.2.17-win32\server\mpm\winnt\mpm_winnt.c
     master_main()

    apache2 使用 apr_proc_create 来创建一个新的进程。在 windows 下,是调用了 CreateProcessAsUserW 函数。

    mpm_winnt.c 是专门针对Windows NT优化的MPM(多路处理模块),它使用一个单独的父进程产生一个单独的子进程,在这个子进程中轮流产生多个线程来处理请求。也就是说 mpm_winnt只能启动父子两个进程, 不能像Linux下那样同时启动多个进程。 mpm_winnt主要通过ThreadsPerChild和MaxRequestsPerChild两个参数来优化Apache,下面详细来说明一下。ThreadsPerChild 这个参数用于设置每个进程的线程数, 子进程在启动时建立这些线程后就不再建立新的线程了. 一方面因为mpm_winnt不能启动多个进程, 所以这个数值要足够大,以便可以处理可能的请求高峰; 另一方面该参数以服务器的响应速度为准的, 数目太大的反而会变慢。因此需要综合均衡一个合理的数值。
    mpm_winnt上的默认值是64, 最大值是1920. 这里建议设置为100-500之间,服务器性能高的话值大一些,反之值小一些。

    /***********************************************************************
     * master_main()
     * master_main() runs in the parent process.  It creates the child
     * process which handles HTTP requests then waits on one of three
     * events:
     *
     * restart_event
     * -------------
     * The restart event causes master_main to start a new child process and
     * tells the old child process to exit (by setting the child_exit_event).
     * The restart event is set as a result of one of the following:
     * 1. An apache -k restart command on the command line
     * 2. A command received from Windows service manager which gets
     *    translated into an ap_signal_parent(SIGNAL_PARENT_RESTART)
     *    call by code in service.c.
     * 3. The child process calling ap_signal_parent(SIGNAL_PARENT_RESTART)
     *    as a result of hitting MaxRequestsPerChild.
     *
     * shutdown_event
     * --------------
     * The shutdown event causes master_main to tell the child process to
     * exit and that the server is shutting down. The shutdown event is
     * set as a result of one of the following:
     * 1. An apache -k shutdown command on the command line
     * 2. A command received from Windows service manager which gets
     *    translated into an ap_signal_parent(SIGNAL_PARENT_SHUTDOWN)
     *    call by code in service.c.
     *
     * child process handle
     * --------------------
     * The child process handle will be signaled if the child process
     * exits for any reason. In a normal running server, the signaling
     * of this event means that the child process has exited prematurely
     * due to a seg fault or other irrecoverable error. For server
     * robustness, master_main will restart the child process under this
     * condtion.
     *
     * master_main uses the child_exit_event to signal the child process
     * to exit.
     **********************************************************************/

     线程的启动

        if ((parent_pid != my_pid) || one_process){
            
    // The child process or in one_process (debug) mode
            ...
            child_main(pconf); ...
        }
        
    else{        
            
    // A real-honest to goodness parent 
            ...
            restart 
    = master_main(ap_server_conf, shutdown_event, restart_event);
            ...
        }

     以上,one_process 是对程序启动的命令行参数 ONE_PROCESS 的判断。这个命令行参数在启动时被保存在全局变量 ap_server_config_defines 中。要想以单进程模式运行(没有控制台)httpd,有两种办法:一种是在命令行下使用  -DONE_PROCESS。另一种是在启动后的钩子中直接将变量 写入 ap_server_config_defines。

        // ap_hook_post_config( ) 钩子中插入以下代码, 
        char **new_start_arg;
        new_start_arg 
    = (char **)apr_array_push(ap_server_config_defines);
        
    *new_start_arg = "ONE_PROCESS";

    需要注意 *new_start_arg 所指向的字串的生命周期。本例中指向一个静态字符串,这个字符串在进程生命周期内有效。如果指向一个栈变量,则可能成为野指针。

     

    void child_main(apr_pool_t *pconf)
    {
        ...
        
    for (i = 0; i < ap_threads_per_child; i++) {
            ...
            child_handles[i] 
    = (HANDLE) _beginthreadex(NULL, (unsigned)ap_thread_stacksize,
                                                           worker_main, (
    void *) i, 0&tid);
            ... 
        }
        create_listener_thread();
       ...
    }

    static void create_listener_thread()
    {
     ...
        
    /* Now start a thread per listener */
        
    for (lr = ap_listeners; lr; lr = lr->next) {
            
    if (lr->sd != NULL) {
                    _beginthreadex(NULL, 
    1000, winnt_accept,(void *) lr, 0&tid);
            }
        }
    }

     线程的终止:

    实战:

    只创建一个监听:

    场景设计:

    将 httpd 配置为多端口监听的情况下, 我们希望能够分辨收到的数据来自哪个端口。httpd 为每个端口创建了一个线程。

    ap_listeners()

    子进程的主要代码:

    child_main 的完整流程
    // child.c
    HANDLE exit_event;
    static int shutdown_in_progress = 0;
    static int workers_may_exit = 0;

    static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
    {
        
    while (1) {
            
    if (workers_may_exit) {
                
    return NULL;
            }
        }
    }
    static unsigned int __stdcall winnt_accept(void *lr_)
    {
        
    while (!shutdown_in_progress) {
                    Sleep(
    100);

        }
        
    if (!shutdown_in_progress) {
            
    /* Yow, hit an irrecoverable error! Tell the child to die. */
            SetEvent(exit_event);
        }
        ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
                     
    "Child %d: Accept thread exiting.", my_pid);
        
    return 0;
    }


    void child_main(apr_pool_t *pconf)
    {
        HANDLE child_events[
    2];
        child_events[
    0= exit_event;
        
    while (1) {
            
    /* Check to see if the child has been told to exit */
            
    if (WaitForSingleObject(exit_event, 0!= WAIT_TIMEOUT) {
                
    break;
            }
            
    /* wait for previous generation to clean up an entry in the scoreboard */
            apr_sleep(
    1 * APR_USEC_PER_SEC);
        }
        
    while (1) {
            rv 
    = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, 1000);
            cld 
    = rv - WAIT_OBJECT_0;
            
    if (rv != WAIT_TIMEOUT && rv != WAIT_FAILED && cld == 0 ) {
                ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
                             
    "Child %d: Exit event signaled. Child process is ending.", my_pid);
                
    break;
            }
        }
        shutdown_in_progress 
    = 1;
        
    /* Tell the worker threads to exit */
        workers_may_exit 
    = 1;
    }
  • 相关阅读:
    javac 小记
    安全专家的工具箱
    MyBatis 缓存机制(十三)
    SpringMVC 环境搭建
    MyBatis 模糊查询的 4 种实现方式
    MyBatis 项目开发中是基于 XML 还是注解?
    MyBatis 动态 SQL 语句中出现 '<' 的问题
    数据库设计的三大范式
    mybatis 同时使用 XML 和注解
    数据库事务
  • 原文地址:https://www.cnblogs.com/diylab/p/1925724.html
Copyright © 2020-2023  润新知