微信公眾號:鄭爾多斯
關注可瞭解更多的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 { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
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_module
的ctx_index
欄位,也即是1ngx_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_module
的infi_conf
鉤子函式為ngx_event_core_init_conf
,並且ngx_epoll_module
的init_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()
不應該是Linux
的API
嗎?如果這裡有epoll_create()
的定義,那麼後續呼叫的epoll_create()
應該是Linux
的API
還是這裡的epoll_create()
?
總結
以一張圖總結一下event
的初始化以及配置檔案的解析過程,
喜歡本文的朋友們,歡迎長按下圖關注訂閱號鄭爾多斯,更多精彩內容第一時間送達