我們這里以單進(jìn)程啟動(dòng)為例
nginx.c
中的main
函數(shù)調(diào)用ngx_single_process_cycle
這個(gè)函數(shù)回循環(huán)調(diào)用
ngx_process_cycle.c
中的
for ( ;; ) {
....
ngx_process_events_and_timers
....
}
事件循環(huán)的核心函數(shù)是 ngx_process_events_and_timers 潘悼。這個(gè)函數(shù)主要干了四件 事情:搶占 accept mutex肄扎,等待并分發(fā)事件壹罚,處理 accept 事件轩端,處理其他io事件
我們這里只介紹等待分發(fā)事件
ngx_event.c
中的
(void) ngx_process_events(cycle, timer, flags);
這里開始 wait并分發(fā)事件吟宦, 我們來可以來看一下這個(gè)函數(shù)
可以看到在 ngx_event.h
中的一個(gè)宏
#define ngx_process_events ngx_event_actions.process_events
我們來看一下 ngx_event_actions 這個(gè)數(shù)據(jù)結(jié)構(gòu)
typedef struct {
/*
添加事件方法枢冤,它將負(fù)責(zé)把1個(gè)感興趣的事件添加到操作系統(tǒng)提供的事件驅(qū)動(dòng)機(jī)制(如epoll伙窃,kqueue等)中澄耍,
這樣,在事件發(fā)生之后箕宙,將可以在調(diào)用下面的process_envets時(shí)獲取這個(gè)事件嚎朽。
*/
ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
/*
刪除事件方法铺纽,它將一個(gè)已經(jīng)存在于事件驅(qū)動(dòng)機(jī)制中的事件一出柬帕,這樣以后即使這個(gè)事件發(fā)生,調(diào)用process_events方法時(shí)也無法再獲取這個(gè)事件
*/
ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
/*
啟用一個(gè)事件狡门,目前事件框架不會(huì)調(diào)用這個(gè)方法陷寝,大部分事件驅(qū)動(dòng)模塊對于該方法的實(shí)現(xiàn)都是與上面的add方法完全一致的
*/
ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
/*
禁用一個(gè)事件,目前事件框架不會(huì)調(diào)用這個(gè)方法其馏,大部分事件驅(qū)動(dòng)模塊對于該方法的實(shí)現(xiàn)都是與上面的del方法一致
*/
ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
/*
向事件驅(qū)動(dòng)機(jī)制中添加一個(gè)新的連接凤跑,這意味著連接上的讀寫事件都添加到事件驅(qū)動(dòng)機(jī)制中了
*/
ngx_int_t (*add_conn)(ngx_connection_t *c);
// 從事件驅(qū)動(dòng)機(jī)制中一出一個(gè)連續(xù)的讀寫事件
ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
// 僅在多線程環(huán)境下會(huì)被調(diào)用,目前叛复,nginx在產(chǎn)品環(huán)境下還不會(huì)以多線程方式運(yùn)行仔引。
ngx_int_t (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
// 在正常的工作循環(huán)中扔仓,將通過調(diào)用process_events方法來處理事件。
// 這個(gè)方法僅在ngx_process_events_and_timers方法中調(diào)用咖耘,它是處理翘簇,分發(fā)事件的核心
ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
// 初始化事件驅(qū)動(dòng)模塊的方法
ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
// 退出事件驅(qū)動(dòng)模塊前調(diào)用的方法。
void (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;
extern ngx_event_actions_t ngx_event_actions;
這個(gè)數(shù)據(jù)結(jié)構(gòu)中定義了很多函數(shù)指針儿倒,
這里我們的配置是
events {
use epoll;
worker_connections 1024; #所以nginx支持的總連接數(shù)就等于worker_processes * worker_connections
}
使用的是 epoll 事件模塊,在epoll 模塊初始化的時(shí)候調(diào)用
ngx_epoll_module.c
中的ngx_epoll_init
的函數(shù)
其中給ngx_event_actions
賦值
ngx_event_actions = ngx_epoll_module_ctx.actions
我們來看下 ngx_epoll_module_ctx
結(jié)構(gòu)類型是
typedef struct {
// 事件模塊的名稱
ngx_str_t *name;
// 在解析配置項(xiàng)前版保,這個(gè)回調(diào)方法用于創(chuàng)建存儲配置項(xiàng)參數(shù)的結(jié)構(gòu)體
void *(*create_conf)(ngx_cycle_t *cycle);
// 在解析配置項(xiàng)完成后,init_conf方法會(huì)被調(diào)用夫否,用于綜合處理當(dāng)前事件模塊感興趣的全部配置項(xiàng)彻犁。
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
// 對于事件驅(qū)動(dòng)機(jī)制,每個(gè)事件模塊需要實(shí)現(xiàn)的10個(gè)抽象方法
ngx_event_actions_t actions;
} ngx_event_module_t;
初始化
//epoll是個(gè)event模塊
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 */
}
};
這些事件處理函數(shù)都在 ngx_epoll_module.c
這個(gè)文件中凰慈,大家可以看一下源碼
綜上汞幢,根據(jù)我們的配置, ngx_event.c
中的 ngx_process_events
實(shí)際調(diào)用的是 ngx_epoll_module.c
中的 ngx_epoll_process_events
這個(gè)函數(shù)有點(diǎn)長微谓,我們找些關(guān)鍵的點(diǎn)看一下急鳄,
//一開始就是等待事件,最長等待時(shí)間為timer堰酿;nginx為事件專門用紅黑樹維護(hù)了一個(gè)計(jì)時(shí)器
events = epoll_wait(ep, event_list, (int) nevents, timer);
所有收集到的事件都放在了event_list 中疾宏,我們來看一下這個(gè)event_list
static struct epoll_event *event_list;
struct epoll_event {
uint32_t events;
epoll_data_t data;
};
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
下面就開始對這些事件就行處理, 先加鎖触创,
ngx_mutex_lock(ngx_posted_events_mutex);
//循環(huán)開始處理收到的所有事件
for (i = 0; i < events; i++) {
c = event_list[i].data.ptr;
instance = (uintptr_t) c & 1;
c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);
rev = c->read;
if (c->fd == -1 || rev->instance != instance) {
/*
* the stale event from a file descriptor
* that was just closed in this iteration
*/
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll: stale event %p", c);
continue;
}
//取得發(fā)生一個(gè)事件
revents = event_list[i].events;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll: fd:%d ev:%04XD d:%p",
c->fd, revents, event_list[i].data.ptr);
//記錄wait的錯(cuò)誤返回狀態(tài)
if (revents & (EPOLLERR|EPOLLHUP)) {
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll_wait() error on fd:%d ev:%04XD",
c->fd, revents);
}
#if 0
if (revents & ~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"strange epoll_wait() events fd:%d ev:%04XD",
c->fd, revents);
}
#endif
//該事件是一個(gè)讀事件坎藐,并該連接上注冊的讀事件是active的
if ((revents & (EPOLLERR|EPOLLHUP))
&& (revents & (EPOLLIN|EPOLLOUT)) == 0)
{
/*
* if the error events were returned without EPOLLIN or EPOLLOUT,
* then add these flags to handle the events at least in one
* active handler
*/
revents |= EPOLLIN|EPOLLOUT;
}
if ((revents & EPOLLIN) && rev->active) {
if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) {
rev->posted_ready = 1;
} else {
rev->ready = 1;
}
//事件放入到相應(yīng)的隊(duì)列中
if (flags & NGX_POST_EVENTS) {
queue = (ngx_event_t **) (rev->accept ?
&ngx_posted_accept_events : &ngx_posted_events);
ngx_locked_post_event(rev, queue);
} else {
rev->handler(rev);
}
}
wev = c->write;
if ((revents & EPOLLOUT) && wev->active) {
if (c->fd == -1 || wev->instance != instance) {
/*
* the stale event from a file descriptor
* that was just closed in this iteration
*/
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll: stale event %p", c);
continue;
}
if (flags & NGX_POST_THREAD_EVENTS) {
wev->posted_ready = 1;
} else {
wev->ready = 1;
}
if (flags & NGX_POST_EVENTS) {
ngx_locked_post_event(wev, &ngx_posted_events);
} else {
wev->handler(wev);
}
}
}
ngx_mutex_unlock(ngx_posted_events_mutex);
先加鎖,對event_list 中的事件循環(huán)處理哼绑,
在每個(gè)循環(huán)中岩馍,
先獲取這個(gè)事件所在的連接, 然后判斷是讀事件還是寫事件抖韩, 然后調(diào)用注冊在這個(gè)事件上的的回調(diào)函數(shù)蛀恩。 讀事件的回調(diào)函數(shù)是
ngx_http_init_connection
這樣就進(jìn)入了 HTTP框架處理流程