### 概述
? ? ? Nginx 是以事件的觸發來驅動的,事件驅動模型主要包括事件收集、事件發送、事件處理(即事件管理)三部分。在Nginx 的工作進程中主要關注的事件是IO 網絡事件 和 定時器事件。在生成的 objs 目錄文件中,其中ngx_modules.c 文件的內容是Nginx 各種模塊的執行順序,我們可以從該文件的內容中看到事件模塊的執行順序為以下所示:注意:由于是在Linux 系統下,所以支持具體的 epoll 事件模塊,接下來的文章結構按照以下順序來寫。
~~~
extern ngx_module_t ngx_events_module;
extern ngx_module_t ngx_event_core_module;
extern ngx_module_t ngx_epoll_module;
~~~
### 事件模塊接口
### ngx_event_module_t 結構體
? ? ? 在 Nginx 中,結構體 ngx_module_t 是 Nginx 模塊最基本的接口。對于每一種不同類型的模塊,都有一個具體的結構體來描述這一類模塊的通用接口,該接口由ngx_module_t 中的成員ctx 管理。在 Nginx 中定義了事件模塊的通用接口ngx_event_module_t 結構體,該結構體定義在文件[src/event/ngx_event.h](http://lxr.nginx.org/source/src/event/ngx_event.h) 中:
~~~
/* 事件驅動模型通用接口ngx_event_module_t結構體 */
typedef struct {
/* 事件模塊名稱 */
ngx_str_t *name;
/* 解析配置項前調用,創建存儲配置項參數的結構體 */
void *(*create_conf)(ngx_cycle_t *cycle);
/* 完成配置項解析后調用,處理當前事件模塊感興趣的全部配置 */
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
/* 每個事件模塊具體實現的方法,有10個方法,即IO多路復用模型的統一接口 */
ngx_event_actions_t actions;
} ngx_event_module_t;
~~~
? ? ? 在 ngx_event_module_t 結構體中actions 的類型是ngx_event_actions_t 結構體,該成員結構實現了事件驅動模塊的具體方法。該結構體定義在文件[src/event/ngx_event.h](http://lxr.nginx.org/source/src/event/ngx_event.h) 中:
~~~
/* IO多路復用模型的統一接口 */
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;
~~~
### ngx_event_t 結構體
? ? ? 在 Nginx 中,每一個具體事件的定義由結構體ngx_event_t 來表示,該結構體ngx_event_t 用來保存具體事件。該結構體定義在文件 [src/event/ngx_event.h](http://lxr.nginx.org/source/src/event/ngx_event.h) 中:
~~~
/* 描述每一個事件的ngx_event_t結構體 */
struct ngx_event_s {
/* 事件相關對象的數據,通常指向ngx_connect_t連接對象 */
void *data;
/* 標志位,為1表示事件可寫,即當前對應的TCP連接狀態可寫 */
unsigned write:1;
/* 標志位,為1表示事件可以建立新連接 */
unsigned accept:1;
/* used to detect the stale events in kqueue, rtsig, and epoll */
unsigned instance:1;
/*
* the event was passed or would be passed to a kernel;
* in aio mode - operation was posted.
*/
/* 標志位,為1表示事件處于活躍狀態 */
unsigned active:1;
/* 標志位,為1表示禁用事件 */
unsigned disabled:1;
/* the ready event; in aio mode 0 means that no operation can be posted */
/* 標志位,為1表示當前事件已經準備就緒 */
unsigned ready:1;
/* 該標志位只用于kqueue,eventport模塊,對Linux上的驅動模塊沒有任何意義 */
unsigned oneshot:1;
/* aio operation is complete */
/* 該標志位用于異步AIO事件處理 */
unsigned complete:1;
/* 標志位,為1表示當前處理的字符流已經結束 */
unsigned eof:1;
/* 標志位,為1表示當前事件處理過程中出錯 */
unsigned error:1;
/* 標志位,為1表示當前事件已超時 */
unsigned timedout:1;
/* 標志位,為1表示當前事件存在于定時器中 */
unsigned timer_set:1;
/* 標志位,為1表示當前事件需要延遲處理 */
unsigned delayed:1;
/*
* 標志位,為1表示TCP建立需要延遲,即完成建立TCP連接的三次握手后,
* 不會立即建立TCP連接,直到接收到數據包才建立TCP連接;
*/
unsigned deferred_accept:1;
/* the pending eof reported by kqueue, epoll or in aio chain operation */
/* 標志位,為1表示等待字符流結束 */
unsigned pending_eof:1;
/* 標志位,為1表示處理post事件 */
unsigned posted:1;
#if (NGX_WIN32)
/* setsockopt(SO_UPDATE_ACCEPT_CONTEXT) was successful */
unsigned accept_context_updated:1;
#endif
#if (NGX_HAVE_KQUEUE)
unsigned kq_vnode:1;
/* the pending errno reported by kqueue */
int kq_errno;
#endif
/*
* kqueue only:
* accept: number of sockets that wait to be accepted
* read: bytes to read when event is ready
* or lowat when event is set with NGX_LOWAT_EVENT flag
* write: available space in buffer when event is ready
* or lowat when event is set with NGX_LOWAT_EVENT flag
*
* iocp: TODO
*
* otherwise:
* accept: 1 if accept many, 0 otherwise
*/
#if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP)
int available;
#else
/* 標志位,在epoll事件機制中表示一次盡可能多地建立TCP連接 */
unsigned available:1;
#endif
/* 當前事件發生時的處理方法 */
ngx_event_handler_pt handler;
#if (NGX_HAVE_AIO)
#if (NGX_HAVE_IOCP)
ngx_event_ovlp_t ovlp;
#else
/* Linux系統aio機制中定義的結構體 */
struct aiocb aiocb;
#endif
#endif
/* epoll機制不使用該變量 */
ngx_uint_t index;
/* 日志記錄 */
ngx_log_t *log;
/* 定時器 */
ngx_rbtree_node_t timer;
/* the posted queue */
ngx_queue_t queue;
/* 標志位,為1表示當前事件已經關閉 */
unsigned closed:1;
/* to test on worker exit */
unsigned channel:1;
unsigned resolver:1;
unsigned cancelable:1;
#if 0
/* the threads support */
/*
* the event thread context, we store it here
* if $(CC) does not understand __thread declaration
* and pthread_getspecific() is too costly
*/
void *thr_ctx;
#if (NGX_EVENT_T_PADDING)
/* event should not cross cache line in SMP */
uint32_t padding[NGX_EVENT_T_PADDING];
#endif
#endif
};
~~~
? ? ? 在每個事件結構體 ngx_event_t 最重要的成員是handler 回調函數,該回調函數定義了當事件發生時的處理方法。該回調方法原型在文件[src/core/ngx_core.h](http://lxr.nginx.org/source/src/core/ngx_core.h) 中:
~~~
typedef void (*ngx_event_handler_pt)(ngx_event_t *ev);
~~~
### ngx_connection_t 結構體
? ? ? 當客戶端向 Nginx 服務器發起連接請求時,此時若Nginx 服務器被動接收該連接,則相對Nginx 服務器來說稱為被動連接,被動連接的表示由基本數據結構體ngx_connection_t 完成。該結構體定義在文件 [src/core/ngx_connection.h](http://lxr.nginx.org/source/src/core/ngx_connection.h) 中:
~~~
/* TCP連接結構體 */
struct ngx_connection_s {
/*
* 當Nginx服務器產生新的socket時,
* 都會創建一個ngx_connection_s 結構體,
* 該結構體用于保存socket的屬性和數據;
*/
/*
* 當連接未被使用時,data充當連接池中空閑連接表中的next指針;
* 當連接被使用時,data的意義由具體Nginx模塊決定;
*/
void *data;
/* 設置該鏈接的讀事件 */
ngx_event_t *read;
/* 設置該連接的寫事件 */
ngx_event_t *write;
/* 用于設置socket的套接字描述符 */
ngx_socket_t fd;
/* 接收網絡字符流的方法,是一個函數指針,指向接收函數 */
ngx_recv_pt recv;
/* 發送網絡字符流的方法,是一個函數指針,指向發送函數 */
ngx_send_pt send;
/* 以ngx_chain_t鏈表方式接收網絡字符流的方法 */
ngx_recv_chain_pt recv_chain;
/* 以ngx_chain_t鏈表方式發送網絡字符流的方法 */
ngx_send_chain_pt send_chain;
/*
* 當前連接對應的ngx_listening_t監聽對象,
* 當前連接由ngx_listening_t成員的listening監聽端口的事件建立;
* 成員connection指向當前連接;
*/
ngx_listening_t *listening;
/* 當前連接已發生的字節數 */
off_t sent;
/* 記錄日志 */
ngx_log_t *log;
/* 內存池 */
ngx_pool_t *pool;
/* 對端的socket地址sockaddr屬性*/
struct sockaddr *sockaddr;
socklen_t socklen;
/* 字符串形式的IP地址 */
ngx_str_t addr_text;
ngx_str_t proxy_protocol_addr;
#if (NGX_SSL)
ngx_ssl_connection_t *ssl;
#endif
/* 本端的監聽端口對應的socket的地址sockaddr屬性 */
struct sockaddr *local_sockaddr;
socklen_t local_socklen;
/* 用于接收、緩存對端發來的字符流 */
ngx_buf_t *buffer;
/*
* 表示將當前連接作為雙向連接中節點元素,
* 添加到ngx_cycle_t結構體的成員
* reuseable_connections_queue的雙向鏈表中;
*/
ngx_queue_t queue;
/* 連接使用次數 */
ngx_atomic_uint_t number;
/* 處理請求的次數 */
ngx_uint_t requests;
unsigned buffered:8;
unsigned log_error:3; /* ngx_connection_log_error_e */
/* 標志位,為1表示不期待字符流結束 */
unsigned unexpected_eof:1;
/* 標志位,為1表示當前連接已經超時 */
unsigned timedout:1;
/* 標志位,為1表示處理連接過程出錯 */
unsigned error:1;
/* 標志位,為1表示當前TCP連接已經銷毀 */
unsigned destroyed:1;
/* 標志位,為1表示當前連接處于空閑狀態 */
unsigned idle:1;
/* 標志位,為1表示當前連接可重用 */
unsigned reusable:1;
/* 標志為,為1表示當前連接已經關閉 */
unsigned close:1;
/* 標志位,為1表示正在將文件的數據發往對端 */
unsigned sendfile:1;
/*
* 標志位,若為1,則表示只有連接對應的發送緩沖區滿足最低設置的閾值時,
* 事件驅動模塊才會分發事件;
*/
unsigned sndlowat:1;
unsigned tcp_nodelay:2; /* ngx_connection_tcp_nodelay_e */
unsigned tcp_nopush:2; /* ngx_connection_tcp_nopush_e */
unsigned need_last_buf:1;
#if (NGX_HAVE_IOCP)
unsigned accept_context_updated:1;
#endif
#if (NGX_HAVE_AIO_SENDFILE)
/* 標志位,為1表示使用異步IO方式將磁盤文件發送給網絡連接的對端 */
unsigned aio_sendfile:1;
unsigned busy_count:2;
/* 使用異步IO發送文件時,用于待發送的文件信息 */
ngx_buf_t *busy_sendfile;
#endif
#if (NGX_THREADS)
ngx_atomic_t lock;
#endif
};
~~~
? ? ? 在處理請求的過程中,若 Nginx 服務器主動向上游服務器建立連接,完成連接建立并與之進行通信,這種相對Nginx 服務器來說是一種主動連接,主動連接由結構體ngx_peer_connection_t 表示,但是該結構體 ngx_peer_connection_t 也是 ngx_connection_t 結構體的封裝。該結構體定義在文件[src/event/ngx_event_connect.h](http://lxr.nginx.org/source/src/event/ngx_event_connect.h) 中:
~~~
/* 主動連接的結構體 */
struct ngx_peer_connection_s {
/* 這里是對ngx_connection_t連接結構體的引用 */
ngx_connection_t *connection;
/* 遠端服務器的socket的地址sockaddr信息 */
struct sockaddr *sockaddr;
socklen_t socklen;
/* 遠端服務器的名稱 */
ngx_str_t *name;
/* 連接重試的次數 */
ngx_uint_t tries;
/* 獲取連接的方法 */
ngx_event_get_peer_pt get;
/* 釋放連接的方法 */
ngx_event_free_peer_pt free;
/* 配合get、free使用 */
void *data;
#if (NGX_SSL)
ngx_event_set_peer_session_pt set_session;
ngx_event_save_peer_session_pt save_session;
#endif
#if (NGX_THREADS)
ngx_atomic_t *lock;
#endif
/* 本地地址信息 */
ngx_addr_t *local;
/* 接收緩沖區 */
int rcvbuf;
/* 記錄日志 */
ngx_log_t *log;
/* 標志位,為1表示connection連接已經緩存 */
unsigned cached:1;
/* ngx_connection_log_error_e */
unsigned log_error:2;
};
~~~
### ngx_events_module?核心模塊
### ngx_events_module?核心模塊的定義
? ? ? ngx_events_module 模塊是事件的核心模塊,該模塊的功能是:定義新的事件類型,并為每個事件模塊定義通用接口ngx_event_module_t 結構體,管理事件模塊生成的配置項結構體,并解析事件類配置項。首先,看下該模塊在文件[src/event/ngx_event.c](http://lxr.nginx.org/source/src/event/ngx_event.c) 中的定義:
~~~
/* 定義事件核心模塊 */
ngx_module_t ngx_events_module = {
NGX_MODULE_V1,
&ngx_events_module_ctx, /* module context */
ngx_events_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
~~~
? ? ? 其中,模塊的配置項指令結構 ngx_events_commands 決定了該模塊的功能。配置項指令結構ngx_events_commands 在文件[src/event/ngx_event.c](http://lxr.nginx.org/source/src/event/ngx_event.c) 中定義如下:
~~~
/* 配置項結構體數組 */
static ngx_command_t ngx_events_commands[] = {
{ ngx_string("events"),
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
ngx_events_block,
0,
0,
NULL },
ngx_null_command
};
~~~
? ? ? 從配置項結構體中可以知道,該模塊只對 events{...} 配置塊感興趣,并定義了管理事件模塊的方法ngx_events_block;ngx_events_block 方法在文件[src/event/ngx_event.c](http://lxr.nginx.org/source/src/event/ngx_event.c) 中定義:
~~~
/* 管理事件模塊 */
static char *
ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
void ***ctx;
ngx_uint_t i;
ngx_conf_t pcf;
ngx_event_module_t *m;
if (*(void **) conf) {
return "is duplicate";
}
/* count the number of the event modules and set up their indices */
/* 計算模塊類中模塊的總數,并初始化模塊在模塊類中的序號 */
ngx_event_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
continue;
}
ngx_modules[i]->ctx_index = ngx_event_max_module++;
}
ctx = ngx_pcalloc(cf->pool, sizeof(void *));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
/* 分配指針數組,用于存儲所有事件模塊生成的配置項結構體指針 */
*ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
if (*ctx == NULL) {
return NGX_CONF_ERROR;
}
*(void **) conf = ctx;
/* 若是事件模塊,并且定義了create_conf方法,則調用該方法創建存儲配置項參數的結構體 */
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
continue;
}
m = ngx_modules[i]->ctx;
if (m->create_conf) {
(*ctx)[ngx_modules[i]->ctx_index] = m->create_conf(cf->cycle);
if ((*ctx)[ngx_modules[i]->ctx_index] == NULL) {
return NGX_CONF_ERROR;
}
}
}
/* 初始化配置項結構體cf */
pcf = *cf;
cf->ctx = ctx;/* 描述事件模塊的配置項結構 */
cf->module_type = NGX_EVENT_MODULE;/* 當前解析指令的模塊類型 */
cf->cmd_type = NGX_EVENT_CONF;/* 當前解析指令的指令類型 */
/* 為所有事件模塊解析配置文件nginx.conf中的event{}塊中的指令 */
rv = ngx_conf_parse(cf, NULL);
*cf = pcf;
if (rv != NGX_CONF_OK)
return rv;
/* 遍歷所有事件模塊,若定義了init_conf方法,則調用該方法用于處理事件模塊感興趣的配置項 */
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
continue;
}
m = ngx_modules[i]->ctx;
if (m->init_conf) {
rv = m->init_conf(cf->cycle, (*ctx)[ngx_modules[i]->ctx_index]);
if (rv != NGX_CONF_OK) {
return rv;
}
}
}
return NGX_CONF_OK;
}
~~~
? ? ? 另外,在 ngx_events_module 模塊的定義中有一個成員ctx 指向了核心模塊的通用接口結構。核心模塊的通用接口結構體定義在文件[src/core/ngx_conf_file.h](http://lxr.nginx.org/source/src/core/ngx_conf_file.h) 中:
~~~
/* 核心模塊的通用接口結構體 */
typedef struct {
/* 模塊名稱 */
ngx_str_t name;
/* 解析配置項前,調用該方法 */
void *(*create_conf)(ngx_cycle_t *cycle);
/* 完成配置項解析后,調用該函數 */
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;
~~~
? ? ? 因此,ngx_events_module 作為核心模塊,必須定義核心模塊的通用接口結構。ngx_events_module 模塊的核心模塊通用接口在文件[src/event/ngx_event.c](http://lxr.nginx.org/source/src/event/ngx_event.c) 中定義:
~~~
/* 實現核心模塊通用接口 */
static ngx_core_module_t ngx_events_module_ctx = {
ngx_string("events"),
NULL,
/*
* 以前的版本這里是NULL,現在實現了一個獲取events配置項的函數,*
* 但是沒有什么作用,因為每個事件模塊都會去獲取events配置項,
* 并進行解析與處理;
*/
ngx_event_init_conf
};
~~~
### 所有事件模塊的配置項管理
? ? ? Nginx 服務器在結構體 ngx_cycle_t 中定義了一個四級指針成員 conf_ctx,整個Nginx 模塊都是使用該四級指針成員管理模塊的配置項結構,以下events 模塊為例對該四級指針成員進行簡單的分析,如下圖所示:

? ? ? 每個事件模塊可以通過宏定義 ngx_event_get_conf 獲取它在create_conf 中分配的結構體的指針;該宏中定義如下:
~~~
#define ngx_event_get_conf(conf_ctx, module) \
(*(ngx_get_conf(conf_ctx, ngx_events_module))) [module.ctx_index];
/* 其中 ngx_get_conf 定義如下 */
#define ngx_get_conf(conf_ctx, module) conf_ctx[module.index]
~~~
? ? ? 從上面的宏定義可以知道,每個事件模塊獲取自己在 create_conf 中分配的結構體的指針,只需在ngx_event_get_conf 傳入參數ngx_cycle_t 中的 conf_ctx 成員,并且傳入自己模塊的名稱即可獲取自己分配的結構體指針。
### ngx_event_core_module 事件模塊
? ? ? ngx_event_core_module 模塊是一個事件類型的模塊,它在所有事件模塊中的順序是第一,是其它事件類模塊的基礎。它主要完成以下任務:
1. 創建連接池;
1. 決定使用哪些事件驅動機制;
1. 初始化將要使用的事件模塊;
### ngx_event_conf_t 結構體
? ? ? ngx_event_conf_t 結構體是用來保存ngx_event_core_module 事件模塊配置項參數的。該結構體在文件[src/event/ngx_event.h](http://lxr.nginx.org/source/src/event/ngx_event.h) 中定義:
~~~
/* 存儲ngx_event_core_module事件模塊配置項參數的結構體 ngx_event_conf_t */
typedef struct {
/* 連接池中最大連接數 */
ngx_uint_t connections;
/* 被選用模塊在所有事件模塊中的序號 */
ngx_uint_t use;
/* 標志位,為1表示可批量建立連接 */
ngx_flag_t multi_accept;
/* 標志位,為1表示打開負載均衡鎖 */
ngx_flag_t accept_mutex;
/* 延遲建立連接 */
ngx_msec_t accept_mutex_delay;
/* 被使用事件模塊的名稱 */
u_char *name;
#if (NGX_DEBUG)
/* 用于保存與輸出調試級別日志連接對應客戶端的地址信息 */
ngx_array_t debug_connection;
#endif
} ngx_event_conf_t;
~~~
### ngx_event_core_module 事件模塊的定義
? ? ? 該模塊在文件 [src/event/ngx_event.c](http://lxr.nginx.org/source/src/event/ngx_event.c) 中定義:
~~~
/* 事件模塊的定義 */
ngx_module_t ngx_event_core_module = {
NGX_MODULE_V1,
&ngx_event_core_module_ctx, /* module context */
ngx_event_core_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
ngx_event_module_init, /* init module */
ngx_event_process_init, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
~~~
? ? ? 其中,模塊的配置項指令結構?ngx_event_core_commands?決定了該模塊的功能。配置項指令結構?ngx_event_core_commands?在文件?[src/event/ngx_event.c](http://lxr.nginx.org/source/src/event/ngx_event.c)?中定義如下:
~~~
static ngx_str_t event_core_name = ngx_string("event_core");
/* 定義ngx_event_core_module 模塊感興趣的配置項 */
static ngx_command_t ngx_event_core_commands[] = {
/* 每個worker進程中TCP最大連接數 */
{ ngx_string("worker_connections"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_connections,
0,
0,
NULL },
/* 與上面的worker_connections配置項相同 */
{ ngx_string("connections"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_connections,
0,
0,
NULL },
/* 選擇事件模塊作為事件驅動機制 */
{ ngx_string("use"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_use,
0,
0,
NULL },
/* 批量接收連接 */
{ ngx_string("multi_accept"),
NGX_EVENT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_event_conf_t, multi_accept),
NULL },
/* 是否打開accept_mutex負載均衡鎖 */
{ ngx_string("accept_mutex"),
NGX_EVENT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_event_conf_t, accept_mutex),
NULL },
/* 打開accept_mutex負載均衡鎖后,延遲處理新連接事件 */
{ ngx_string("accept_mutex_delay"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
0,
offsetof(ngx_event_conf_t, accept_mutex_delay),
NULL },
/* 對指定IP的TCP連接打印debug級別的調試日志 */
{ ngx_string("debug_connection"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_debug_connection,
0,
0,
NULL },
ngx_null_command
};
~~~
? ? ? 其中,每個事件模塊都需要實現事件模塊的通用接口結構 ngx_event_module_t,ngx_event_core_module?模塊的上下文結構?ngx_event_core_module_ctx?并不真正的負責網絡事件的驅動,所有不會實現ngx_event_module_t 結構體中的成員 actions 中的方法。上下文結構?ngx_event_core_module_ctx?在文件?[src/event/ngx_event.c](http://lxr.nginx.org/source/src/event/ngx_event.c)?中定義如下:
~~~
/* 根據事件模塊通用接口,實現ngx_event_core_module事件模塊的上下文結構 */
ngx_event_module_t ngx_event_core_module_ctx = {
&event_core_name,
ngx_event_core_create_conf, /* create configuration */
ngx_event_core_init_conf, /* init configuration */
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};
~~~
? ? ? 在模塊定義中,實現了兩種方法分別為 ngx_event_module_init 和ngx_event_process_init 方法。在Nginx 啟動過程中沒有使用 fork 出 worker 子進程之前,先調用?ngx_event_core_module?模塊中的?ngx_event_module_init 方法,當fork 出 worker 子進程后,每一個 worker 子進程則會調用?ngx_event_process_init 方法。
? ? ? ngx_event_module_init 方法在文件[src/event/ngx_event.c](http://lxr.nginx.org/source/src/event/ngx_event.c) 中定義:
~~~
/* 初始化事件模塊 */
static ngx_int_t
ngx_event_module_init(ngx_cycle_t *cycle)
{
void ***cf;
u_char *shared;
size_t size, cl;
ngx_shm_t shm;
ngx_time_t *tp;
ngx_core_conf_t *ccf;
ngx_event_conf_t *ecf;
/* 獲取存儲所有事件模塊配置結構的指針數據的首地址 */
cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);
/* 獲取事件模塊ngx_event_core_module的配置結構 */
ecf = (*cf)[ngx_event_core_module.ctx_index];
/* 在錯誤日志中輸出被使用的事件模塊名稱 */
if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
"using the \"%s\" event method", ecf->name);
}
/* 獲取模塊ngx_core_module的配置結構 */
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
ngx_timer_resolution = ccf->timer_resolution;
#if !(NGX_WIN32)
{
ngx_int_t limit;
struct rlimit rlmt;
/* 獲取當前進程所打開的最大文件描述符個數 */
if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"getrlimit(RLIMIT_NOFILE) failed, ignored");
} else {
/*
* 當前事件模塊的連接數大于最大文件描述符個數,
* 或者大于由配置文件nginx.conf指定的worker_rlinit_nofile設置的最大文件描述符個數時,
* 出錯返回;
*/
if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur
&& (ccf->rlimit_nofile == NGX_CONF_UNSET
|| ecf->connections > (ngx_uint_t) ccf->rlimit_nofile))
{
limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ?
(ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile;
ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
"%ui worker_connections exceed "
"open file resource limit: %i",
ecf->connections, limit);
}
}
}
#endif /* !(NGX_WIN32) */
/*
* 模塊ngx_core_module的master進程為0,表示不創建worker進程,
* 則初始化到此結束,并成功返回;
*/
if (ccf->master == 0) {
return NGX_OK;
}
/*
* 若master不為0,且存在負載均衡鎖,則表示初始化完畢,并成功返回;
*/
if (ngx_accept_mutex_ptr) {
return NGX_OK;
}
/* 不滿足以上兩個條件,則初始化下列變量 */
/* cl should be equal to or greater than cache line size */
/* 緩存行的大小 */
cl = 128;
/*
* 統計需要創建的共享內存大小;
* ngx_accept_mutex用于多個worker進程之間的負載均衡鎖;
* ngx_connection_counter表示nginx處理的連接總數;
* ngx_temp_number表示在連接中創建的臨時文件個數;
*/
size = cl /* ngx_accept_mutex */
+ cl /* ngx_connection_counter */
+ cl; /* ngx_temp_number */
#if (NGX_STAT_STUB)
/*
* 下面表示某種情況的連接數;
* ngx_stat_accepted 表示已成功建立的連接數;
* ngx_stat_handled 表示已獲取ngx_connection_t結構并已初始化讀寫事件的連接數;
* ngx_stat_requests 表示已被http模塊處理過的連接數;
* ngx_stat_active 表示已獲取ngx_connection_t結構體的連接數;
* ngx_stat_reading 表示正在接收TCP字符流的連接數;
* ngx_stat_writing 表示正在發送TCP字符流的連接數;
* ngx_stat_waiting 表示正在等待事件發生的連接數;
*/
size += cl /* ngx_stat_accepted */
+ cl /* ngx_stat_handled */
+ cl /* ngx_stat_requests */
+ cl /* ngx_stat_active */
+ cl /* ngx_stat_reading */
+ cl /* ngx_stat_writing */
+ cl; /* ngx_stat_waiting */
#endif
/* 初始化共享內存信息 */
shm.size = size;
shm.name.len = sizeof("nginx_shared_zone");
shm.name.data = (u_char *) "nginx_shared_zone";
shm.log = cycle->log;
/* 創建共享內存 */
if (ngx_shm_alloc(&shm) != NGX_OK) {
return NGX_ERROR;
}
/* 獲取共享內存的首地址 */
shared = shm.addr;
ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;
/* -1表示以非阻塞模式獲取共享內存鎖 */
ngx_accept_mutex.spin = (ngx_uint_t) -1;
if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared,
cycle->lock_file.data)
!= NGX_OK)
{
return NGX_ERROR;
}
/* 初始化變量 */
ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);
(void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"counter: %p, %d",
ngx_connection_counter, *ngx_connection_counter);
ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);
tp = ngx_timeofday();
ngx_random_number = (tp->msec << 16) + ngx_pid;
#if (NGX_STAT_STUB)
ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl);
ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl);
ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl);
ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);
ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);
ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);
ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);
#endif
return NGX_OK;
}
~~~
? ? ? ngx_event_process_init 方法在文件[src/event/ngx_event.c](http://lxr.nginx.org/source/src/event/ngx_event.c) 中定義:
~~~
static ngx_int_t
ngx_event_process_init(ngx_cycle_t *cycle)
{
ngx_uint_t m, i;
ngx_event_t *rev, *wev;
ngx_listening_t *ls;
ngx_connection_t *c, *next, *old;
ngx_core_conf_t *ccf;
ngx_event_conf_t *ecf;
ngx_event_module_t *module;
/* 獲取ngx_core_module核心模塊的配置結構 */
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
/* 獲取ngx_event_core_module事件核心模塊的配置結構 */
ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
/*
* 在事件核心模塊啟用accept_mutex鎖的情況下,
* 只有在master-worker工作模式并且worker進程數量大于1,
* 此時,才確定進程啟用負載均衡鎖;
*/
if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
ngx_use_accept_mutex = 1;
ngx_accept_mutex_held = 0;
ngx_accept_mutex_delay = ecf->accept_mutex_delay;
} else {/* 否則關閉負載均衡鎖 */
ngx_use_accept_mutex = 0;
}
#if (NGX_WIN32)
/*
* disable accept mutex on win32 as it may cause deadlock if
* grabbed by a process which can't accept connections
*/
ngx_use_accept_mutex = 0;
#endif
ngx_queue_init(&ngx_posted_accept_events);
ngx_queue_init(&ngx_posted_events);
/* 初始化由紅黑樹實現的定時器 */
if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
return NGX_ERROR;
}
/* 根據use配置項所指定的事件模塊,調用ngx_actions_t中的init方法初始化事件模塊 */
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;
}
#if !(NGX_WIN32)
/*
* NGX_USE_TIMER_EVENT只有在eventport和kqueue事件模型中使用,
* 若配置文件nginx.conf設置了timer_resolution配置項,
* 并且事件模型不為eventport和kqueue時,調用settimer方法,
*/
if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
struct sigaction sa;
struct itimerval itv;
ngx_memzero(&sa, sizeof(struct sigaction));
/*
* ngx_timer_signal_handler的實現如下:
* void ngx_timer_signal_handler(int signo)
* {
* ngx_event_timer_alarm = 1;
* }
* ngx_event_timer_alarm 為1時表示需要更新系統時間,即調用ngx_time_update方法;
* 更新完系統時間之后,該變量設為0;
*/
/* 指定信號處理函數 */
sa.sa_handler = ngx_timer_signal_handler;
/* 初始化信號集 */
sigemptyset(&sa.sa_mask);
/* 捕獲信號SIGALRM */
if (sigaction(SIGALRM, &sa, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigaction(SIGALRM) failed");
return NGX_ERROR;
}
/* 設置時間精度 */
itv.it_interval.tv_sec = ngx_timer_resolution / 1000;
itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;
itv.it_value.tv_sec = ngx_timer_resolution / 1000;
itv.it_value.tv_usec = (ngx_timer_resolution % 1000 ) * 1000;
/* 使用settimer函數發送信號 SIGALRM */
if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"setitimer() failed");
}
}
/* 對poll、/dev/poll、rtsig事件模塊的特殊處理 */
if (ngx_event_flags & NGX_USE_FD_EVENT) {
struct rlimit rlmt;
if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"getrlimit(RLIMIT_NOFILE) failed");
return NGX_ERROR;
}
cycle->files_n = (ngx_uint_t) rlmt.rlim_cur;
cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n,
cycle->log);
if (cycle->files == NULL) {
return NGX_ERROR;
}
}
#endif
/* 預分配連接池 */
cycle->connections =
ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
if (cycle->connections == NULL) {
return NGX_ERROR;
}
c = cycle->connections;
/* 預分配讀事件結構,讀事件個數與連接數相同 */
cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
cycle->log);
if (cycle->read_events == NULL) {
return NGX_ERROR;
}
rev = cycle->read_events;
for (i = 0; i < cycle->connection_n; i++) {
rev[i].closed = 1;
rev[i].instance = 1;
}
/* 預分配寫事件結構,寫事件個數與連接數相同 */
cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
cycle->log);
if (cycle->write_events == NULL) {
return NGX_ERROR;
}
wev = cycle->write_events;
for (i = 0; i < cycle->connection_n; i++) {
wev[i].closed = 1;
}
i = cycle->connection_n;
next = NULL;
/* 按照序號,將讀、寫事件與連接對象對應,即設置到每個ngx_connection_t 對象中 */
do {
i--;
c[i].data = next;
c[i].read = &cycle->read_events[i];
c[i].write = &cycle->write_events[i];
c[i].fd = (ngx_socket_t) -1;
next = &c[i];
#if (NGX_THREADS)
c[i].lock = 0;
#endif
} while (i);
/* 設置空閑連接鏈表 */
cycle->free_connections = next;
cycle->free_connection_n = cycle->connection_n;
/* for each listening socket */
/* 為所有ngx_listening_t監聽對象中的connections成員分配連接,并設置讀事件的處理方法 */
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
/* 為監聽套接字分配連接,并設置讀事件 */
c = ngx_get_connection(ls[i].fd, cycle->log);
if (c == NULL) {
return NGX_ERROR;
}
c->log = &ls[i].log;
c->listening = &ls[i];
ls[i].connection = c;
rev = c->read;
rev->log = c->log;
rev->accept = 1;
#if (NGX_HAVE_DEFERRED_ACCEPT)
rev->deferred_accept = ls[i].deferred_accept;
#endif
if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
if (ls[i].previous) {
/*
* delete the old accept events that were bound to
* the old cycle read events array
*/
old = ls[i].previous->connection;
if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT)
== NGX_ERROR)
{
return NGX_ERROR;
}
old->fd = (ngx_socket_t) -1;
}
}
#if (NGX_WIN32)
if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
ngx_iocp_conf_t *iocpcf;
rev->handler = ngx_event_acceptex;
if (ngx_use_accept_mutex) {
continue;
}
if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) {
return NGX_ERROR;
}
ls[i].log.handler = ngx_acceptex_log_error;
iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module);
if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex)
== NGX_ERROR)
{
return NGX_ERROR;
}
} else {
rev->handler = ngx_event_accept;
if (ngx_use_accept_mutex) {
continue;
}
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
}
#else
/* 為監聽端口的讀事件設置處理方法ngx_event_accept */
rev->handler = ngx_event_accept;
if (ngx_use_accept_mutex) {
continue;
}
if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
if (ngx_add_conn(c) == NGX_ERROR) {
return NGX_ERROR;
}
} else {
/* 將監聽對象連接的讀事件添加到事件驅動模塊中 */
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
}
#endif
}
return NGX_OK;
}
~~~
參考資料:
《深入理解 Nginx 》
《[nginx事件模塊分析(二)](http://blog.csdn.net/freeinfor/article/details/16343223)》
- 前言
- Nginx 配置文件
- Nginx 內存池管理
- Nginx 基本數據結構
- Nginx 數組結構 ngx_array_t
- Nginx 鏈表結構 ngx_list_t
- Nginx 隊列雙向鏈表結構 ngx_queue_t
- Nginx 哈希表結構 ngx_hash_t
- Nginx 紅黑樹結構 ngx_rbtree_t
- Nginx 模塊開發
- Nginx 啟動初始化過程
- Nginx 配置解析
- Nginx 中的 upstream 與 subrequest 機制
- Nginx 源碼結構分析
- Nginx 事件模塊
- Nginx 的 epoll 事件驅動模塊
- Nginx 定時器事件
- Nginx 事件驅動模塊連接處理
- Nginx 中 HTTP 模塊初始化
- Nginx 中處理 HTTP 請求
- Nginx 中 upstream 機制的實現
- Nginx 中 upstream 機制的負載均衡