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.
**********************************************************************/
线程的启动
// 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。
char **new_start_arg;
new_start_arg = (char **)apr_array_push(ap_server_config_defines);
*new_start_arg = "ONE_PROCESS";
需要注意 *new_start_arg 所指向的字串的生命周期。本例中指向一个静态字符串,这个字符串在进程生命周期内有效。如果指向一个栈变量,则可能成为野指针。
{
...
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);
...
}
...
}
{
...
/* 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()
子进程的主要代码:
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;
}
范晨鹏
------------------
软件是一种态度
成功是一种习惯