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()

 

子进程的主要代码:

 

ExpandedBlockStart.gif 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 ;
}

 

 

你可能感兴趣的:(apache2 进程/线程模型)