您的位置:首页 > 运维架构 > Nginx

nginx源码分析(15)-模块分析(1)

2009-10-28 11:43 507 查看
nginx的内部实现上用四个模块上下文结构把所有模块从实现上分开为四种,不同的模块上下文支持不同的模块钩子。如果抛开实现方式,仅仅从功能逻辑上来
区分,大致可以把所有模块分为三类,第一类是nginx的内核模块;第二类是http模块;第三类是mail模块。内核模块包含核心类模块、event类
模块和没有模块上下文结构的conf模块;http模块是所有的http类模块;mail模块是所有的mail类模块。由此可见,按照功能分类基本上与按
照模块上下文分类一致。

核心类模块,即上下文为ngx_core_module_t结构的模块有以下几个:

ngx_module_t ngx_core_module;

ngx_module_t ngx_errlog_module;

ngx_module_t ngx_events_module;

ngx_module_t ngx_http_module;

ngx_module_t ngx_mail_module;

ngx_module_t ngx_openssl_module;

ngx_module_t ngx_google_perftools_module;

ngx_module_t ngx_conf_module;(没有模块上下文,这里把它归类到核心类,便于后面分析)

ngx_events_module,ngx_http_module,ngx_mail_module
这三个模块主要是实现了对配置文件中events {...},http {...},mail {...}/imap
{...}配置域的解析钩子,它们嵌套调用event类模块、http类模块和mail类模块实现的指令解析钩子解析{}中的具体指令,它们没有实现模块
上下文的create_conf和init_conf钩子(也不需要,因为它们只是一个中转而已,不做实际的事情)。

ngx_core_module
实现了配置文件中全局域的大部分指令的解析钩子,它们也实现模块上下文的create_conf钩子和init_conf钩子,创建和初始化
ngx_core_conf_t配置结构,这个结构可以通过cycle->conf_ctx[module.index]引用到。

ngx_errlog_module实现了配置文件中全局域的error_log指令的解析钩子,没有实现模块上下文的create_conf和init_conf钩子。

ngx_conf_module实现了全局域的include指令的解析钩子,没有实现模块上下文的create_conf和init_conf钩子。

ngx_openssl_module
实现了全局域的ssl_engine指令的解析钩子,实现了模块上下文的create_conf钩子,创建ngx_openssl_conf_t配置结
构,这个结构可以通过cycle->conf_ctx[module.index]引用到,也实现了模块的exit_master钩子。

事件类模块,即模块上下文为ngx_event_module_t结构的模块有以下几个:

ngx_module_t ngx_event_core_module;

ngx_module_t ngx_select_module;

ngx_module_t ngx_poll_module;

ngx_module_t ngx_eventport_module;

ngx_module_t ngx_aio_module;

ngx_module_t ngx_epoll_module;

ngx_module_t ngx_kqueue_module;

ngx_module_t ngx_devpoll_module;

ngx_module_t ngx_rtsig_module;

ngx_event_core_module
实现了配置文件events域的
worker_connections,connections,use,multi_accept,accept_mutex,accept_mutex_delay
和debug_connection指令的解析钩子,也实现了模块上下文的create_conf和init_conf钩子,创建和初始化
ngx_event_conf_t配置结构,这个结构可以通过cycle->conf_ctx[module.index]引用到,另外还实现了模
块的init_module和init_process钩子。

ngx_select_module实现了模块上下文的init_conf
初始化配置钩子和actions.add、actions.del、actions.enable、actions.disable、
actions.process_events、actions.init、actions.done这几个事件处理钩子。

ngx_poll_module
实现了模块上下文的init_conf初始化配置钩子和actions.add、actions.del、actions.enable、
actions.disable、actions.process_events、actions.init、actions.done这几个事件处理钩
子。

ngx_eventport_module实现了配置文件events域的eventport_events指令的解析钩子,实现了
模块上下文的create_conf和init_conf钩子,创建和初始化ngx_eventport_conf_t配置结构体,这个结构可以通过
cycle->conf_ctx[module.index]引用到,也实现了actions.add、actions.del、
actions.enable、actions.disable、actions.process_events、actions.init、
actions.done这几个事件处理钩子。

ngx_aio_module实现了actions.add、actions.del、actions.del_conn、actions.process_events、actions.init、actions.done这几个事件处理钩子。

ngx_epoll_module
实现了配置文件events域的epoll_events指令的解析钩子,实现了模块上下文的create_conf和init_conf钩子,创建和初
始化ngx_epoll_conf_t配置结构,这个结构可以通过cycle->conf_ctx[module.index]引用到,也实现了
actions.add、actions.del、actions.enable、actions.disable、actions.add_conn、
actions.del_conn、actions.process_events、actions.init、actions.done这几个事件处理
钩子。

ngx_kqueue_module实现了配置文件events域的kqueue_changes和kqueue_events指
令的解析钩子,实现了模块上下文的create_conf和init_conf钩子,创建和初始化ngx_kqueue_conf_t配置结构,这个结构
可以通过cycle->conf_ctx[module.index]引用到,也实现了actions.add、actions.del、
actions.enable、actions.disable、actions.process_changes、
actions.process_events、actions.init、actions.done这几个事件处理钩子。

ngx_devpoll_module
实现了配置文件events域的devpoll_changes和devpoll_events指令的解析钩子,实现了模块上下文的
create_conf和init_conf钩子,创建和初始化ngx_devpoll_conf_t配置结构,这个结构可以通过
cycle->conf_ctx[module.index]引用到,也实现了actions.add、actions.del、
actions.enable、actions.disable、actions.process_events、actions.init、
actions.done这几个事件处理钩子。

ngx_rtsig_module实现了配置文件events域的rtsig_前缀的几个
指令的解析钩子,实现了模块上下文的create_conf和init_conf钩子,创建和初始化ngx_rtsig_conf_t配置结构,这个结构
可以通过cycle->conf_ctx[module.index]引用到,也实现了actions.add_conn、
actions.del_conn、actions.process_events、actions.init、actions.done这几个事件处理
钩子。

除了ngx_event_core_module之外,其余的模块是根据具体的操作系统和IO模型定制的事件处理机制,在支持epoll的linux系统上,nginx默认会选择epoll模块。


么多的内核模块,其实我们只要重点分析几个:ngx_events_module、ngx_event_core_module和
ngx_epoll_module,因为分析这两个模块就能够掌握nginx最重要的事件处理机制,其余的那些要么很简单,要么不适用,简单了解就可以
了。

nginx的事件处理机制也是一个很重要的内容,和进程模型一样,我们也会采用代码注释的方式分析。nginx的事件处理机制可以分
为构建和运行两个阶段。构建的过程从分析配置指令就开始了,直到worker工作进程调用
ngx_process_events_and_timers(cycle)循环处理之前。

在ngx_init_cycle解析配置文件的时候,当解析到“events {”的时候,将会调用ngx_events_module的events指令解析钩子ngx_events_block。

static char *

ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

    char                 *rv;

    void               ***ctx;

    ngx_uint_t            i;

    ngx_conf_t            pcf;

    ngx_event_module_t   *m;

    /* count the number of the event modules and set up their indices */

    // 对event类模块点一下数

    ngx_event_max_module = 0;

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->type != NGX_EVENT_MODULE) {

            continue;

        }

        ngx_modules[i]->ctx_index = ngx_event_max_module++;

    }

    ctx = ngx_pcalloc(cf->pool, sizeof(void *));

    if (ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));

    if (*ctx == NULL) {

        return NGX_CONF_ERROR;

    }

    *(void **) conf = ctx;

    // 调用所有event类模块的create_conf钩子,创建配置结构,这些配置结构形成一个数组

    // 这个数组的指针最终会赋给cycle->conf_ctx,这是一个void ****指针,可以把所有

    // 模块的配置结构有层次的保存下来

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->type != NGX_EVENT_MODULE) {

            continue;

        }

        m = ngx_modules[i]->ctx;

        if (m->create_conf) {

            (*ctx)[ngx_modules[i]->ctx_index] = m->create_conf(cf->cycle);

            if ((*ctx)[ngx_modules[i]->ctx_index] == NULL) {

                return NGX_CONF_ERROR;

            }

        }

    }

    pcf = *cf;

    cf->ctx = ctx;

    cf->module_type = NGX_EVENT_MODULE;

    cf->cmd_type = NGX_EVENT_CONF;

    // 解析“events {}”块中的指令集

    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;

    if (rv != NGX_CONF_OK)

        return rv;

    // 调用每个event类模块的init_conf钩子,初始化配置结构,这些配置结构有些已经在解析指令的时候被

    // 填充,这里主要是处理那些还没有被指令填充,或者填充的有问题的那些

    for (i = 0; ngx_modules[i]; i++) {

        if (ngx_modules[i]->type != NGX_EVENT_MODULE) {

            continue;

        }

        m = ngx_modules[i]->ctx;

        if (m->init_conf) {

            rv = m->init_conf(cf->cycle, (*ctx)[ngx_modules[i]->ctx_index]);

            if (rv != NGX_CONF_OK) {

                return rv;

            }

        }

    }

    return NGX_CONF_OK;

}

在ngx_init_cycle的最后,将会调用到ngx_event_core_module模块的init_module钩子ngx_event_module_init,这个函数的处理过程如下:

1、若配置文件中没有events section,函数出错退出;

2、设置时间精度:ngx_timer_resolution = ccf->timer_resolution;

3、检查一下worker_connections是否超过了进程允许的最大描述符字,超过的话写一条waring日志;

4、若master_process off,退出;if(ccf->master == 0) { return NGX_OK; }

5、if(ngx_accept_mutex_ptr) { return NGX_OK; }

6、创建共享内存,用于accept mutex和connection counter:

    /* cl should be equal or bigger than cache line size */

    cl = 128;

    size = cl            /* ngx_accept_mutex */

           + cl;         /* ngx_connection_counter */

    ...

    shm.size = size;

    shm.name.len = sizeof("nginx_shared_zone");

    shm.name.data = (u_char *) "nginx_shared_zone";

    shm.log = cycle->log;

    if (ngx_shm_alloc(&shm) != NGX_OK) {

        return NGX_ERROR;

    }

    shared = shm.addr;

    // 创建accept mutex

    ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;

    // 系统支持原子数据则使用原子数据实现accept mutex,否则使用文件上锁实现

    if (ngx_shmtx_create(&ngx_accept_mutex, shared, cycle->lock_file.data)

        != NGX_OK)

    {

        return NGX_ERROR;

    }

    // 创建connection counter

    ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);

    ...

    (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

                   "counter: %p, %d",

                   ngx_connection_counter, *ngx_connection_counter);


目前为止,一些配置已经被设置了,这些配置包括全局的cycle,不同模块的配置结构ngx_<module
name>_conf_t(cycle->conf_ctx可以引用到),cycle->connection_n被设置为
worker_connections(最大连接数),ngx_event_conf_t.use被设置为选中的(use)事件处理模块的偏移
值:module->ctx_index。

在每个worker初始化的时候,会调用到ngx_event_core_module的init_process钩子ngx_event_process_init,这个函数会设置cycle的大部分配置,处理过程如下:

1、设置accpet mutex相关的全局变量:

    if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {

        ngx_use_accept_mutex = 1;

        ngx_accept_mutex_held = 0;

        ngx_accept_mutex_delay = ecf->accept_mutex_delay;

    } else {

        ngx_use_accept_mutex = 0;

    }   

2、调用ngx_event_timer_init(cycle->log),初始化事件的计时机制,这里用到了红黑树,详细分析留待以后;

3、调用被use的事件处理机制模块的actions.init钩子,linux上使用的是ngx_epoll_module,其actions.init钩子为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);

    // 创建一个epoll句柄,指定监听数目为cycle->connection_n / 2

    if (ep == -1) {

        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;

        }  

    }  

    // event_list是被监听的句柄的列表,默认最大为512个句柄

    if (nevents < epcf->events) {

        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;

        }  

    }  

    nevents = epcf->events;

    ngx_io = ngx_os_io;

    // 可以便捷的访问到事件处理模块的各种actions钩子

    ngx_event_actions = ngx_epoll_module_ctx.actions;

#if (NGX_HAVE_CLEAR_EVENT)

    ngx_event_flags = NGX_USE_CLEAR_EVENT

#else

    ngx_event_flags = NGX_USE_LEVEL_EVENT

#endif

                      |NGX_USE_GREEDY_EVENT

                      |NGX_USE_EPOLL_EVENT;

    return NGX_OK;

}

4、计时器创建和初始化:

    if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {

        struct sigaction  sa; 

        struct itimerval  itv;

        ngx_memzero(&sa, sizeof(struct sigaction));

        sa.sa_handler = ngx_timer_signal_handler;

        sigemptyset(&sa.sa_mask);

        // 设置SIGALRM的信号处理函数,这个信号初始函数调用ngx_time_update(0, 0)(调用gettimeofday)修改时间cache

        if (sigaction(SIGALRM, &sa, NULL) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "sigaction(SIGALRM) failed");

            return NGX_ERROR;

        }   

        itv.it_interval.tv_sec = ngx_timer_resolution / 1000;

        itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;

        itv.it_value.tv_sec = ngx_timer_resolution / 1000;

        itv.it_value.tv_usec = (ngx_timer_resolution % 1000 ) * 1000;

        // 设置定时器,间隔一定时间发送SIGALRM信号,可以参考settimer系统调用的手册页

        if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "setitimer() failed");

        }   

    }   

5、对poll,/dev/poll,rtsig三个事件处理模块的特殊处理,不深究:

if (ngx_event_flags & NGX_USE_FD_EVENT) {

        struct rlimit  rlmt;

        if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,

                          "getrlimit(RLIMIT_NOFILE) failed");

            return NGX_ERROR;

        }   

        cycle->files_n = (ngx_uint_t) rlmt.rlim_cur;

        cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n,

                                  cycle->log);

        if (cycle->files == NULL) {

            return NGX_ERROR;

        }   

    }   

6、创建cycle->connection_n个ngx_connection_t结构变量,让cycle->connections指向它;

7、分别创建cycle->connection_n个读和写事件结构变量(ngx_event_t)并初始化其状态,让cycle->read_events和cycle->write_events指向它们;

8、把cycle->connections中各个元素串成一个链表(用ngx_connection_t.data指向下一个),并让cycle->free_connections指向头部第一个元素,即:

cycle->free_connections = &cycle->connections[0],cycle->free_connection_n = cycle->connection_n,这样就构建了一条空闲的连接链表;

9、为cycle->listening中的每个监听套接字设置初始的连接以及事件资源。


此,事件处理机制构建过程完成了,然而理解事件处理机制上面的分析依然不够,下一篇会从另外一个角度,从分析事件处理机制中涉及的几个重要的数据结构入
手:ngx_listening_t、ngx_connection_t、ngx_event_t。这些数据结构构成了nginx事件处理机制的基础。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息