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來進行選擇默認的。這個函數也比較簡單這裏就不列出來了。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章