这是我的页面头部

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;
}

 

 

posted @ 2011-01-19 10:16  范晨鹏  阅读(1960)  评论(0编辑  收藏  举报