nginx事件模块之IO多路复用的选择

nginx是基于事件驱动的,也就是说nginx内部都是通过事件的发生来驱动的,如果没有事件,nginx就是阻塞在epoll_wait,select,sigsuspend等这样的系统调用上。各种操作系统对事件的处理及管理都是不同的。而且每种操作系统的IO多路复用的实现方式也是各不相同。而nginx是一种跨平台的高性能的web server,那它是怎样把各种操作系统的IO多路复用集合在一起,又是怎样让他们根据自己的系统进行选择的呢?下面来讲述下。

nginx可以让用户根据自己的需求选择IO复用的方式,也就是use命令。如果需要使用epoll,只需要在配置文件中添加这样一句话:

use epoll
注意:这句话需要加到event配置块中。

那这个use命令是怎么工作的呢?下面根据源码来讲述use的工作过程,在讲述use的工作过程之前,现介绍一个比较重要的结构体:

typedef struct {
    ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

    ngx_int_t  (*add_conn)(ngx_connection_t *c);
    ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);

    ngx_int_t  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
    ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
                   ngx_uint_t flags);

    ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
    void       (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;
这个结构体都是函数指针。这些指针的作用其实很简单,从指针命就能看的出来。这里就不一一介绍了。

其实搜索nginx的几种io复用(nginx使用io复用都是通过模块实现的,而io复用的模块的文件一般是这么命名的ngx_epoll_module.c)实现方式,你会发现每种io复用都给这个结构体赋值了。在继续向下介绍之前不得不提一个全局变量,那就是:

ngx_event_actions
这个变量就是ngx_event_actions_t的类型。这个全局变量就决定了nginx使用哪种io复用方式。

下面就来讲述use的实现过程。配置文件配置了use epoll,那nginx就会调用ngx_event_use,下面列出了这个函数比较重要的一块源码:

 for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;
        if (module->name->len == value[1].len) {
            if (ngx_strcmp(module->name->data, value[1].data) == 0) {
                ecf->use = ngx_modules[m]->ctx_index;
                ecf->name = module->name->data;

                if (ngx_process == NGX_PROCESS_SINGLE
                    && old_ecf
                    && old_ecf->use != ecf->use)
                {
                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "when the server runs without a master process "
                               "the \"%V\" event type must be the same as "
                               "in previous configuration - \"%s\" "
                               "and it cannot be changed on the fly, "
                               "to change it you need to stop server "
                               "and start it again",
                               &value[1], old_ecf->name);

                    return NGX_CONF_ERROR;
                }

                return NGX_CONF_OK;
            }
        }
    }
注意标红的那句话,ngx_modules[m]->ctx_index这个应该很熟悉了,它是当前模块在“event模块这个数组”中的索引。ecf->use被赋值为与use指令后面值对应名字相同的模块的索引,也就是例子中epoll在“event数组中的索引值”。那这个ecf->use有什么作用呢?继续看下面这个函数

static ngx_int_t
ngx_event_process_init(ngx_cycle_t *cycle)
{
ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
......
for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
            continue;
        }

        if (ngx_modules[m]->ctx_index != ecf->use) {
            continue;
        }

        module = ngx_modules[m]->ctx;

        if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
            /* fatal */
            exit(2);
        }

        break;
    }
......
}
这个函数会在worker进程初始化的时候调用。当你看到那句被标红的语句,应该就知道是怎么回事了吧。通过对比ecf->use来调用与其相等的模块的ngx_event_actions_t的init函数,也就是epoll中的ngx_epoll_init。下面来看下ngx_epoll_init的源码:

ngx_event_actions = ngx_epoll_module_ctx.actions;
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 */
    }
};
上面那么多讲述use选择自己需要的io复用方式。

如果不使用use,那ngin会怎么选取io复用呢,nginx会通过ngx_event_core_init_conf来进行选择默认的。这个函数也比较简单这里就不列出来了。






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值