Nginx 的工作进程

2016-1-11 chenhui Nginx

Nginx 的进程分为监控进程和工作进程以及缓存进程,监控进程是 Nginx 的主进程,主要是作为用户和工作进程的中转站,并维护工作进程的数量,如果有工作进程被杀死,则重新建立一个。工作进程则往往有多个,是被监控进程 fork() 出来的,用于处理实际的业务逻辑。而缓存进程,自然就是处理缓存工作的。

当然,Nginx 也可以单进程运行,此时主进程作为工作进程。


Nginx 的主进程执行的 main() 函数调用了 ngx_master_process_cycle() 函数,ngx_master_process_cycle() 又调用了 ngx_start_worker_processes() 来创建所有的工作进程,然后进入一个无限循环等待事件的发生。


static void
ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
{
    ....
    for (i = 0; i < n; i++) {

        ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL,
                          "worker process", type);

        ...
    }
}


ngx_start_worker_processes() 执行一个循环,这个循环的次数就是要创建的工作进程数,默认情况下是 1,在循环中,会调用到 ngx_spawn_process() 来创建进程。


ngx_pid_t
ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
    char *name, ngx_int_t respawn)
{
    ...
	
    pid = fork();

    switch (pid) {

    case -1:
        ...
		
    case 0:
        ngx_pid = ngx_getpid();
        proc(cycle, data);
        break;

    default:
        break;
    }

    ...

    return pid;
}


创建的方式当然是使用 fork(),进程创建后执行的函数为 ngx_worker_process_cycle(),工作进程在 ngx_worker_process_cycle() 里运行一个死循环,他就在这个循环里接收并处理用户请求。 


static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{

    for ( ;; ) {

        ...

        // 初始化工作进程,这里会初始化 epoll,即 epoll_create
        ngx_worker_process_init(cycle, 1);

        ...
	
        // 开始等待并处理事件,即 epoll_wait
        ngx_process_events_and_timers(cycle);

        ....
    }
}


ngx_process_events_and_timers() 这个函数用来接收处理用户的请求,他会调用到具体技术的阻塞点。比如 epoll 的 epoll_wait(),select 的 select(),poll 的 poll()。


void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
    ...
	
    (void) ngx_process_events(cycle, timer, flags);
	
	...
}


这个 ngx_process_events 是一个宏,定义如下:


#define ngx_process_events   ngx_event_actions.process_events


这里又涉及到了 ngx_event_actions 这个对象。实际上,Nginx 就是使用这个对象来兼容不同的平台的。epoll、kqueue、select、poll 等异步技术都有与之对应的对象存在,对于 epoll,根据搜索可得知,这个对象在 src/event/modules/ngx_epoll_module.c 这个文件里的 ngx_epoll_init() 函数里注册


static ngx_int_t
ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
{
    ngx_epoll_conf_t  *epcf;
 
    epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);
 
    if (ep == -1) {
	//创建 epoll 句柄
        ep = epoll_create(cycle->connection_n / 2);
 
        if (ep == -1) {
            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                          "epoll_create() failed");
            return NGX_ERROR;
        }
		
	...
    }
	
    if (nevents < epcf->events) {
	//这里很重要,他申请了 epoll_event 数组,即存放所有监听的 fd 的数组。
        if (event_list) {
            ngx_free(event_list);
        }

        event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events,
                               cycle->log);
        if (event_list == NULL) {
            return NGX_ERROR;
        }
    }
	
    ...
 
    ngx_event_actions = ngx_epoll_module_ctx.actions;
 
	...
	
    return NGX_OK;
}

从函数名可看出,这个函数应该就是 Nginx 对 epoll 的支持的初始化函数,他先创建了 epoll 句柄,然后让 ngx_event_actions 指向 ngx_epoll_module_ctx.actions,这是一个 ngx_event_modulet 类型的对象。这样我们就知道他到底调用了哪个了。

先来看一下 ngx_event_module_t 的定义。


typedef struct {
    ngx_str_t              *name;

    void                 *(*create_conf)(ngx_cycle_t *cycle);
    char                 *(*init_conf)(ngx_cycle_t *cycle, void *conf);

    ngx_event_actions_t     actions;
} ngx_event_module_t;


然后再来看 ngx_epoll_module_ctx 的定义,这样我们就知道 Nginx 对 epoll 的调用了。


ngx_event_module_t  ngx_epoll_module_ctx = {
    &epoll_name,
    ngx_epoll_create_conf,               /* create configuration */
    ngx_epoll_init_conf,                 /* init configuration */

    {
        ngx_epoll_add_event,             /* add an event */
        ngx_epoll_del_event,             /* delete an event */
        ngx_epoll_add_event,             /* enable an event */
        ngx_epoll_del_event,             /* disable an event */
        ngx_epoll_add_connection,        /* add an connection */
        ngx_epoll_del_connection,        /* delete an connection */
        NULL,                            /* process the changes */
        ngx_epoll_process_events,        /* process the events */
        ngx_epoll_init,                  /* init the events */
        ngx_epoll_done,                  /* done the events */
    }
};


第四个那一大堆函数,就是 epoll 的回调了。其中 ngx_process_events 调用到的是第三个,也就是 ngx_epoll_process_events() 这个函数。


static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
    int                events;
    uint32_t           revents;
    ngx_int_t          instance, i;
	...
	
    events = epoll_wait(ep, event_list, (int) nevents, timer);

	...
	
    for (i = 0; i < events; i++) {
        c = event_list[i].data.ptr;

        instance = (uintptr_t) c & 1;
        c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);

        rev = c->read;

        if (c->fd == -1 || rev->instance != instance) {

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "epoll: stale event %p", c);
            continue;
        }

        revents = event_list[i].events;

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "epoll: fd:%d ev:%04XD d:%p",
                       c->fd, revents, event_list[i].data.ptr);

        if (revents & (EPOLLERR|EPOLLHUP)) {
            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "epoll_wait() error on fd:%d ev:%04XD",
                           c->fd, revents);
        }

        if ((revents & (EPOLLERR|EPOLLHUP))
             && (revents & (EPOLLIN|EPOLLOUT)) == 0)
        {
            revents |= EPOLLIN|EPOLLOUT;
        }

        if ((revents & EPOLLIN) && rev->active) {

            ...
        }

        wev = c->write;

        if ((revents & EPOLLOUT) && wev->active) {

            ...
        }
    }

    ngx_mutex_unlock(ngx_posted_events_mutex);

    return NGX_OK;
}


ngx_epoll_process_events() 一上来就是一个熟悉的 epoll_wait(),然后再是一个熟悉的遍历fd,并根据相应的事件做不同的处理。至于怎么处理的,这里先不谈,本文主要讲解工作进程的执行流程。








发表评论:

Copyright ©2015-2016 freehui All rights reserved