Nginx源碼分析-Epoll模塊(轉載)
轉載自:http://www.tbdata.org/archives/1296Nginx源碼分析-Epoll模塊
3 comments 十二月 26th, 2010 | by yixiao in 高性能服務器
Linux平臺上,Nginx使用epoll完成事件驅動,實現高并發;本文將不對epoll本身進行介紹(網上一堆一堆的文章介紹epoll的原理及使用方法,甚至源碼分析等),僅看一下Nginx是如何使用epoll的。
Nginx在epoll模塊中定義了好幾個函數,這些函數基本都是作為回調注冊到事件抽象層的對應接口上,從而實現了事件驅動的具體化,我們看如下的一段代碼:
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 */ } };
這段代碼就是epoll的相關函數注冊到事件抽象層,這里所謂的事件抽象層在前面的博文中有提過,就是Nginx為了方便支持和開發具體的I/O模型,從而實現的一層抽象。代碼后面的注釋將功能說明得很詳細了,本文就只重點關注ngx_epoll_init和ngx_epoll_process_events兩個函數,其他幾個函數就暫且忽略了。
ngx_epoll_init主要是完成epoll的相關初始化工作,代碼分析如下:
static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer) { ngx_epoll_conf_t *epcf; /*取得epoll模塊的配置結構*/ epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module); /*ep是epoll模塊定義的一個全局變量,初始化為-1*/ if (ep == -1) { /*創一個epoll對象,容量為總連接數的一半*/ ep = epoll_create(cycle->connection_n / 2); if (ep == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "epoll_create() failed"); return NGX_ERROR; } } /*nevents也是epoll模塊定義的一個全局變量,初始化為0*/ if (nevents events) { if (event_list) { ngx_free(event_list); } /*event_list存儲產生事件的數組*/ event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events, cycle->log); if (event_list == NULL) { return NGX_ERROR; } } nevents = epcf->events; /*初始化全局變量ngx_io, ngx_os_is定義為: ngx_os_io_t ngx_os_io = { ngx_unix_recv, ngx_readv_chain, ngx_udp_unix_recv, ngx_unix_send, ngx_writev_chain, 0 };(位于src/os/unix/ngx_posix_init.c) */ ngx_io = ngx_os_io; /*這里就是將epoll的具體接口函數注冊到事件抽象層接口ngx_event_actions上。 具體是上文提到的ngx_epoll_module_ctx中封裝的如下幾個函數 ngx_epoll_add_event, ngx_epoll_del_event, ngx_epoll_add_event, ngx_epoll_del_event, ngx_epoll_add_connection, ngx_epoll_del_connection, ngx_epoll_process_events, ngx_epoll_init, ngx_epoll_done, */ ngx_event_actions = ngx_epoll_module_ctx.actions; #if (NGX_HAVE_CLEAR_EVENT) /*epoll將添加這個標志,主要為了實現邊緣觸發*/ ngx_event_flags = NGX_USE_CLEAR_EVENT #else /*水平觸發*/ ngx_event_flags = NGX_USE_LEVEL_EVENT #endif |NGX_USE_GREEDY_EVENT /*io的時候,直到EAGAIN為止*/ |NGX_USE_EPOLL_EVENT; /*epoll標志*/ return NGX_OK; }
epoll初始化工作沒有想象中的復雜,和我們平時使用epoll都一樣,下面看ngx_epoll_process_events,這個函數主要用來完成事件的等待并處理。
static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) { int events; uint32_t revents; ngx_int_t instance, i; ngx_uint_t level; ngx_err_t err; ngx_log_t *log; ngx_event_t *rev, *wev, **queue; ngx_connection_t *c; /*一開始就是等待事件,最長等待時間為timer;nginx為事件 專門用紅黑樹維護了一個計時器。后續對這個timer單獨分析。 */ events = epoll_wait(ep, event_list, (int) nevents, timer); if (events == -1) { err = ngx_errno; } else { err = 0; } if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) { /*執行一次時間更新, nginx將時間緩存到了一組全局變量中,方便程序高效的獲取事件。*/ ngx_time_update(); } /*處理wait錯誤*/ if (err) { if (err == NGX_EINTR) { if (ngx_event_timer_alarm) { ngx_event_timer_alarm = 0; return NGX_OK; } level = NGX_LOG_INFO; } else { level = NGX_LOG_ALERT; } ngx_log_error(level, cycle->log, err, "epoll_wait() failed"); return NGX_ERROR; } /*wait返回事件數0,可能是timeout返回,也可能是非timeout返回;非timeout返回則是error*/ if (events == 0) { if (timer != NGX_TIMER_INFINITE) { return NGX_OK; } ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "epoll_wait() returned no events without timeout"); return NGX_ERROR; } log = cycle->log; /*for循環開始處理收到的所有事件*/ for (i = 0; i read; 。。。。。。。。。。。。。 /*取得發生一個事件*/ revents = event_list[i].events; /*記錄wait的錯誤返回狀態*/ if (revents & (EPOLLERR|EPOLLHUP)) { ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, "epoll_wait() error on fd:%d ev:%04XD", c->fd, revents); } 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; } /*該事件是一個讀事件,并該連接上注冊的讀事件是active的*/ if ((revents & EPOLLIN) && rev->active) { if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) { rev->posted_ready = 1; } else { rev->ready = 1; } /*事件放入相應的隊列中;關于此處的先入隊再處理,在前面的文章中已經介紹過了。*/ 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 (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); } } } return NGX_OK; }
本文將關注的兩個epoll函數也就這么一點代碼了,但整個epoll還有添加事件和刪除事件等的相關函數,代碼都很簡單,本文就不做具體的分析了。
posted on 2011-07-10 00:54 周強 閱讀(952) 評論(0) 編輯 收藏 引用 所屬分類: nginx