nginx 事件模塊簡單剖析

我們這里以單進(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框架處理流程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市茂浮,隨后出現(xiàn)的幾起案子双谆,更是在濱河造成了極大的恐慌,老刑警劉巖席揽,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件顽馋,死亡現(xiàn)場離奇詭異,居然都是意外死亡幌羞,警方通過查閱死者的電腦和手機(jī)寸谜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來属桦,“玉大人熊痴,你說我怎么就攤上這事他爸。” “怎么了果善?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵讲逛,是天一觀的道長。 經(jīng)常有香客問我岭埠,道長盏混,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任惜论,我火速辦了婚禮许赃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘馆类。我一直安慰自己混聊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布乾巧。 她就那樣靜靜地躺著句喜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沟于。 梳的紋絲不亂的頭發(fā)上咳胃,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機(jī)與錄音旷太,去河邊找鬼展懈。 笑死,一個(gè)胖子當(dāng)著我的面吹牛供璧,可吹牛的內(nèi)容都是我干的存崖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼睡毒,長吁一口氣:“原來是場噩夢啊……” “哼来惧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起演顾,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤供搀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后偶房,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體趁曼,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年棕洋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乒融。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡掰盘,死狀恐怖摄悯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情愧捕,我是刑警寧澤奢驯,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站次绘,受9級特大地震影響瘪阁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜邮偎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一管跺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧禾进,春花似錦豁跑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至宠纯,卻和暖如春卸夕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背婆瓜。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工娇哆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人勃救。 一個(gè)月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓碍讨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蒙秒。 傳聞我的和親對象是個(gè)殘疾皇子勃黍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容