nginx事件模組 -- 第二篇
微信公眾號:關注可瞭解更多的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_tngx_event_core_module_ctx = { 2&event_core_name, 3ngx_event_core_create_conf,/* create configuration */ 4ngx_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{ 4ngx_event_conf_t*ecf; 5 6ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t)); 7if (ecf == NULL) { 8return NULL; 9} 10 11ecf->connections = NGX_CONF_UNSET_UINT; 12ecf->use = NGX_CONF_UNSET_UINT; 13ecf->multi_accept = NGX_CONF_UNSET; 14ecf->accept_mutex = NGX_CONF_UNSET; 15ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC; 16ecf->name = (void *) NGX_CONF_UNSET; 17 18#if (NGX_DEBUG) 19 20if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4, 21sizeof(ngx_cidr_t)) == NGX_ERROR) 22{ 23return NULL; 24} 25 26#endif 27 28return ecf; 29} 複製程式碼
這個程式碼真的是很簡單了,沒有什麼可分析的,功能如下:
生成一個 ngx_event_conf_t
結構體,然後將該結構體的所有欄位都賦予一個預設值
ngx_epoll_module模組
我們再看一下 ngx_epoll_module
的原始碼,結合起來分析:
1static ngx_event_module_tngx_epoll_module_ctx = { 2&epoll_name, 3ngx_epoll_create_conf,/* create configuration */ 4ngx_epoll_init_conf,/* init configuration */ 5 6{ 7ngx_epoll_add_event,/* add an event */ 8ngx_epoll_del_event,/* delete an event */ 9ngx_epoll_add_event,/* enable an event */ 10ngx_epoll_del_event,/* disable an event */ 11ngx_epoll_add_connection,/* add an connection */ 12ngx_epoll_del_connection,/* delete an connection */ 13#if (NGX_HAVE_EVENTFD) 14ngx_epoll_notify,/* trigger a notify */ 15#else 16NULL,/* trigger a notify */ 17#endif 18ngx_epoll_process_events,/* process the events */ 19ngx_epoll_init,/* init the events */ 20ngx_epoll_done,/* done the events */ 21} 22}; 複製程式碼
ngx_epoll_module
的鉤子函式為 ngx_epoll_create_conf()
,這個函式的原始碼非常非常非常簡單,這裡不再分析,作用也很簡單,就是建立一個 ngx_epoll_conf_t
結構體,並對結構體的欄位進行賦初值。
解析use欄位
我們觀察配置檔案中和 event
相關的內容,如下:
1events { 2worker_connections1024; 3use epoll; 4} 複製程式碼
配置檔案比較簡單,符合我們的一貫原則,越簡單越容易分析,哈哈。
我們從原始碼中找到處理 use
欄位的內容,看下面:point_down:
1 { ngx_string("use"), 2NGX_EVENT_CONF|NGX_CONF_TAKE1, 3ngx_event_use, 40, 50, 6NULL }, 複製程式碼
從中我們可以知道,處理 use
欄位的處理函式為 ngx_event_use()
,下面我們分析一下這個處理函式,我們刪除了部分用於健壯性判斷語句,以及一些除錯語句。
1static char * 2ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 3{ 4ngx_event_conf_t*ecf = conf; 5 6ngx_int_tm; 7ngx_str_t*value; 8ngx_event_conf_t*old_ecf; 9ngx_event_module_t*module; 10 11// 防止出現多個use配置指令 12if (ecf->use != NGX_CONF_UNSET_UINT) { 13return "is duplicate"; 14} 15 16value = cf->args->elts; 17 18if (cf->cycle->old_cycle->conf_ctx) { 19old_ecf = ngx_event_get_conf(cf->cycle->old_cycle->conf_ctx, 20ngx_event_core_module); 21} else { 22old_ecf = NULL; 23} 24 25//遍歷所有的`NGX_EVENT_MODULE`模組 26for (m = 0; cf->cycle->modules[m]; m++) { 27if (cf->cycle->modules[m]->type != NGX_EVENT_MODULE) { 28continue; 29} 30 31module = cf->cycle->modules[m]->ctx; 32if (module->name->len == value[1].len) { 33if (ngx_strcmp(module->name->data, value[1].data) == 0) { 34ecf->use = cf->cycle->modules[m]->ctx_index; 35ecf->name = module->name->data; 36return NGX_CONF_OK; 37} 38} 39} 40 41ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 42"invalid event type \"%V\"", &value[1]); 43 44return NGX_CONF_ERROR; 45} 複製程式碼
從程式碼中我們可以得到如下結論:
-
ngx_event_conf_t
結構體的use
欄位標識我們選擇的事件模組的ctx_index
,在這裡就是ngx_epoll_module
的ctx_index
欄位,也即是1 -
ngx_event_conf_t
結構體的name
欄位標識我們選擇的事件模組的name
,在這裡就是epoll
解析worker_connections欄位
同理,我們先從原始碼中找到 worker_connections
有關的程式碼:
1{ ngx_string("worker_connections"), 2NGX_EVENT_CONF|NGX_CONF_TAKE1, 3ngx_event_connections, 40, 50, 6NULL } 複製程式碼
顯然,處理函式為 ngx_event_connections()
,如下:
1ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 2{ 3ngx_event_conf_t*ecf = conf; 4 5ngx_str_t*value; 6 7if (ecf->connections != NGX_CONF_UNSET_UINT) { 8return "is duplicate"; 9} 10 11value = cf->args->elts; 12ecf->connections = ngx_atoi(value[1].data, value[1].len); 13if (ecf->connections == (ngx_uint_t) NGX_ERROR) { 14ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 15"invalid number \"%V\"", &value[1]); 16 17return NGX_CONF_ERROR; 18} 19 20cf->cycle->connection_n = ecf->connections; 21 22return 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
的初始化以及配置檔案的解析過程,

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