nginx事件模組 -- 第二篇

鄭爾多斯發表於2018-12-15

微信公眾號:鄭爾多斯
關注可瞭解更多的Nginx知識。任何問題或建議,請公眾號留言;
關注公眾號,有趣有內涵的文章第一時間送達!

事件機制

上一篇檔案我們簡單的介紹了ngx_event_block()函式的功能,這個函式用於解析events指令,引入事件機制。其實真正的工作是在ngx_event_core_module中完成的,這個模組可以解析use,work_connections等指令,這些指令用於控制nginx事件機制的一些引數。
上一篇文章中我們也提到過執行ngx_event_block()函式的時候會遍歷所有的NGX_EVENT_MODULE型別的模組,然後呼叫他們的create_conf()方法,然後解析events指令,解析完畢之後會呼叫所有NGX_EVENT_MODULE型別的模組的init_conf()方法。這一片文章我們就分析一下這一過程。

ngx_event_core_module模組

這個模組是非常重要的,它是第一個NGX_EVENT_MODULE型別的模組,它真正的引入了事件機制。我們可以通過use指令選擇我們將要使用的事件模型。使用worker_connections等指令設定相關的事件引數。
下面我們看一下這個模組的原始碼:

1static ngx_event_module_t  ngx_event_core_module_ctx = {
2    &event_core_name,
3    ngx_event_core_create_conf,            /* create configuration */
4    ngx_event_core_init_conf,              /* init configuration */
5
6    { NULLNULLNULLNULLNULLNULLNULLNULLNULLNULL }
7};
複製程式碼

從原始碼中我們可以知道對應的鉤子函式分別為ngx_event_core_core_conf()ngx_event_core_init_conf()。我們逐個分析:

 1static void *
2ngx_event_core_create_conf(ngx_cycle_t *cycle)
3
{
4    ngx_event_conf_t  *ecf;
5
6    ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));
7    if (ecf == NULL) {
8        return NULL;
9    }
10
11    ecf->connections = NGX_CONF_UNSET_UINT;
12    ecf->use = NGX_CONF_UNSET_UINT;
13    ecf->multi_accept = NGX_CONF_UNSET;
14    ecf->accept_mutex = NGX_CONF_UNSET;
15    ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;
16    ecf->name = (void *) NGX_CONF_UNSET;
17
18#if (NGX_DEBUG)
19
20    if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4,
21                       sizeof(ngx_cidr_t)) == NGX_ERROR)
22    {
23        return NULL;
24    }
25
26#endif
27
28    return ecf;
29}
複製程式碼

這個程式碼真的是很簡單了,沒有什麼可分析的,功能如下:

生成一個ngx_event_conf_t結構體,然後將該結構體的所有欄位都賦予一個預設值

ngx_epoll_module模組

我們再看一下ngx_epoll_module的原始碼,結合起來分析:

 1static ngx_event_module_t  ngx_epoll_module_ctx = {
2    &epoll_name,
3    ngx_epoll_create_conf,               /* create configuration */
4    ngx_epoll_init_conf,                 /* init configuration */
5
6    {
7        ngx_epoll_add_event,             /* add an event */
8        ngx_epoll_del_event,             /* delete an event */
9        ngx_epoll_add_event,             /* enable an event */
10        ngx_epoll_del_event,             /* disable an event */
11        ngx_epoll_add_connection,        /* add an connection */
12        ngx_epoll_del_connection,        /* delete an connection */
13#if (NGX_HAVE_EVENTFD)
14        ngx_epoll_notify,                /* trigger a notify */
15#else
16        NULL,                            /* trigger a notify */
17#endif
18        ngx_epoll_process_events,        /* process the events */
19        ngx_epoll_init,                  /* init the events */
20        ngx_epoll_done,                  /* done the events */
21    }
22};
複製程式碼

ngx_epoll_module的鉤子函式為ngx_epoll_create_conf(),這個函式的原始碼非常非常非常簡單,這裡不再分析,作用也很簡單,就是建立一個ngx_epoll_conf_t結構體,並對結構體的欄位進行賦初值。

解析use欄位

我們觀察配置檔案中和event相關的內容,如下:

1events {
2    worker_connections  1024;
3    use epoll;
4}
複製程式碼

配置檔案比較簡單,符合我們的一貫原則,越簡單越容易分析,哈哈。
我們從原始碼中找到處理use欄位的內容,看下面?

1 { ngx_string("use"),
2      NGX_EVENT_CONF|NGX_CONF_TAKE1,
3      ngx_event_use,
4      0,
5      0,
6      NULL },
複製程式碼

從中我們可以知道,處理use欄位的處理函式為ngx_event_use(),下面我們分析一下這個處理函式,我們刪除了部分用於健壯性判斷語句,以及一些除錯語句。

 1static char *
2ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
3
{
4    ngx_event_conf_t  *ecf = conf;
5
6    ngx_int_t             m;
7    ngx_str_t            *value;
8    ngx_event_conf_t     *old_ecf;
9    ngx_event_module_t   *module;
10
11// 防止出現多個use配置指令
12    if (ecf->use != NGX_CONF_UNSET_UINT) {
13        return "is duplicate";
14    }
15
16    value = cf->args->elts;
17
18    if (cf->cycle->old_cycle->conf_ctx) {
19        old_ecf = ngx_event_get_conf(cf->cycle->old_cycle->conf_ctx,
20                                     ngx_event_core_module);
21    } else {
22        old_ecf = NULL;
23    }
24
25//遍歷所有的`NGX_EVENT_MODULE`模組
26    for (m = 0; cf->cycle->modules[m]; m++) {
27        if (cf->cycle->modules[m]->type != NGX_EVENT_MODULE) {
28            continue;
29        }
30
31        module = cf->cycle->modules[m]->ctx;
32        if (module->name->len == value[1].len) {
33            if (ngx_strcmp(module->name->data, value[1].data) == 0) {
34                ecf->use = cf->cycle->modules[m]->ctx_index;
35                ecf->name = module->name->data;
36                return NGX_CONF_OK;
37            }
38        }
39    }
40
41    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
42                       "invalid event type \"%V\"", &value[1]);
43
44    return NGX_CONF_ERROR;
45}
複製程式碼

從程式碼中我們可以得到如下結論:

  • ngx_event_conf_t結構體的use欄位標識我們選擇的事件模組的ctx_index,在這裡就是ngx_epoll_modulectx_index欄位,也即是1
  • ngx_event_conf_t結構體的name欄位標識我們選擇的事件模組的name,在這裡就是epoll

解析worker_connections欄位

同理,我們先從原始碼中找到worker_connections有關的程式碼:

1{ ngx_string("worker_connections"),
2      NGX_EVENT_CONF|NGX_CONF_TAKE1,
3      ngx_event_connections,
4      0,
5      0,
6      NULL }
複製程式碼

顯然,處理函式為ngx_event_connections(),如下:

 1ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2{
3    ngx_event_conf_t  *ecf = conf;
4
5    ngx_str_t  *value;
6
7    if (ecf->connections != NGX_CONF_UNSET_UINT) {
8        return "is duplicate";
9    }
10
11    value = cf->args->elts;
12    ecf->connections = ngx_atoi(value[1].data, value[1].len);
13    if (ecf->connections == (ngx_uint_t) NGX_ERROR) {
14        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
15                           "invalid number \"%V\"", &value[1]);
16
17        return NGX_CONF_ERROR;
18    }
19
20    cf->cycle->connection_n = ecf->connections;
21
22    return NGX_CONF_OK;
23}
複製程式碼

函式太簡單,沒法分析,就是為ngx_event_conf_t結構體的connections欄位賦值。這裡就是1024.

呼叫init_conf()

從上面的程式碼中可以知道,ngx_event_core_moduleinfi_conf鉤子函式為ngx_event_core_init_conf,並且ngx_epoll_moduleinit_conf鉤子函式為ngx_epoll_init_conf函式。這兩個函式都很簡單,但是ngx_event_core_init_conf函式我沒有看明白是什麼意思,這個可能後面還要查證一下,暫定。

待解決問題

在分析的過程中,有下面兩個問題沒有搞清楚,要待查證:

  • ngx_event_core_init_conf()函式有什麼作用?為什麼在配置檔案已經解析之後還要對ngx_event_conf_t結構體中的一些欄位重新賦值?如果這樣的話,配置檔案中的相同配置有什麼作用?
  • ngx_epoll_module中的epoll_create()函式為什麼直接return -1;?epoll_create()不應該是LinuxAPI嗎?如果這裡有epoll_create()的定義,那麼後續呼叫的epoll_create()應該是LinuxAPI還是這裡的epoll_create()

總結

以一張圖總結一下event的初始化以及配置檔案的解析過程,

event解析
event解析

喜歡本文的朋友們,歡迎長按下圖關注訂閱號鄭爾多斯,更多精彩內容第一時間送達

鄭爾多斯
鄭爾多斯

相關文章