nginx中的事件模塊是一個很重要的模塊,但這裏作爲初讀,我們只簡單看一下ngx_event的數據結構,至於模塊和機制,留作之後再分析。
下面是結構體ngx_event_t的代碼:
typedef struct ngx_event_s ngx_event_t;
struct ngx_event_s {
void *data;
/* 事件上下文數據,通常data都是指向ngx_connection_t連接對象。
* 開啓文件異步I/O時,它可能會指向ngx_event_aio_t結構體。
* ... ngx_int_t ngx_handle_write_event(ngx_event_t *wev, size_t lowat) {
ngx_connection_t *c;
if (lowat) {
c = wev->data; // 在這裏event的data就指向了connection_t,直接用指針獲取
if (ngx_send_lowat(c, lowat) == NGX_ERROR) {
return NGX_ERROR;
}
}...} ...
unsigned write:1;
/* 標誌位,爲1時表示事件是可寫的。通常它表示對應的TCP連接可寫,也就是連接處於可以發送網絡包的狀態。*/
unsigned accept:1;
/* 標誌位,爲1時表示爲此事件可以建立新的連接。通常在ngx_cycle_t中的listening動態數組中,
每一個監聽對象ngx_listening_t,對應的讀事件中的accept標誌位纔會是1。*/
unsigned instance:1;
/* used to detect the stale events in kqueue, rtsig, and epoll
* 這個標誌位用於區分當前事件是否過期,它僅僅是給事件驅動模塊使用的,而事件消費模塊可不用關心。
* 爲什麼需要這個標誌位呢?當開始處理一批事件時,處理前面的事件可能會關閉一些連接,
而這些連接有可能影響這批事件中還未處理到的後面的事件,這時可通過instance來避免處理後面的過期事件。
*/
unsigned active:1;
/* the event was passed or would be passed to a kernel;
* in aio mode - operation was posted.
* 標誌位,爲1表示當前事件是活躍的,爲0表示事件是不活躍的。
* 這個狀態對應着事件驅動模塊處理方式的不同。例如,在添加事件,刪除事件和處理事件時,
active標誌位的不同都會對應着不同的處理方式。在使用事件時,一般不會直接改變active標誌位。
* ... if (!rev->delayed) {
if (rev->active && !rev->ready) {
ngx_add_timer(rev, p->read_timeout);
} else if (rev->timer_set) {
ngx_del_timer(rev);
}
} ...
*/
unsigned disabled:1;
/* 標誌位,爲1表示禁用事件,僅在kqueue或者rtsig事件驅動模塊中有效,對於epoll事件驅動模塊則沒有意義。
* ... if (c->read->active || c->read->disabled) {
ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);
} ... // 位於close_connection函數中
*/
/
unsigned ready:1;
/* the ready event; in aio mode 0 means that no operation can be posted
* 標誌位,爲1表示當前事件準備就緒,也就是說,允許這個事件的handler處理這個事件。
* 在HTTP框架中,經常會檢查事件的ready標誌位,以確定是否可以接收請求或者發送相應。
* ... if (rev->ready) {
if (ngx_use_accept_mutex) {
ngx_post_event(rev, &ngx_posted_events);
return;
}
rev->handler(rev);
return;
} ...
*/
unsigned oneshot:1;
/* 該標誌位僅對kqueue,eventport等模塊有意義,而對於linux上的epoll事件驅動模塊則是無意義的。*/
unsigned complete:1;
/* aio operation is complete
* 用於異步aio事件的處理 */
unsigned eof:1;
unsigned error:1;
/* 標誌位,eof表示當前處理的字符流已經結束,error表示事件處理過程出錯了。
* ... flags = (rev->eof || rev->error) ? NGX_CLOSE_EVENT : 0; ...
*/
unsigned timedout:1;
/* 標誌位,爲1表示這個事件超時,用以提示handler做超時處理,它與timer_set都用了定時器
* ... if (wev->timedout) {
wev->timedout = 0;
ngx_http_perl_handle_request(r);
return;
} ...
*/
unsigned timer_set:1;
/* 標誌位,爲1表示這個事件存在於定時器中
* ... if (!ngx_cleaner_event.timer_set) {
ngx_add_timer(&ngx_cleaner_event, 30000);
ngx_cleaner_event.timer_set = 1;
} ...
*/
unsigned delayed:1;
/* 標誌位,delayed爲1表示需要延遲處理這個事件,它僅用於限速功能 */
unsigned deferred_accept:1;
/* 標誌位,爲1表示延遲建立TCP連接,也就是TCP三次握手後並不建立連接,而是等到真正收到數據包後才建連接 */
unsigned pending_eof:1;
/* the pending eof reported by kqueue, epoll or in aio chain operation
* 標誌位,爲1表示等待字符流結束,它只與kqueue和aio事件驅動機制有關 */
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
unsigned available:1;
#endif
ngx_event_handler_pt handler;
/* 這個事件發生時的處理方法,每個事件處理模塊都會重新實現它 */
#if (NGX_HAVE_AIO)
// win下的一種事件驅動模型
#if (NGX_HAVE_IOCP)
ngx_event_ovlp_t ovlp;
#else
// 在linux中定義的aio結構體
struct aiocb aiocb;
#endif
#endif
ngx_uint_t index;
/* epoll 事件驅動方式不使用index */
ngx_log_t *log;
/* 記錄當前event的log對象 */
ngx_rbtree_node_t timer;
/* 定時器節點,用於定時器紅黑樹中 */
/* the posted queue */
ngx_queue_t queue;
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
};
我們再看一下event.h裏一個數據結構:
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);
/* 添加/移出事件方法,負責把事件添加/移出到操作系統提供的事件驅動機制(如epoll,kqueue等)中,
這樣在事件發生之後,將可以/無法調用下面的process_envets時獲取這個事件。*/
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);
/* 啓用/禁用一個事件,目前事件框架不會調用,大部分事件驅動模塊對該方法的實現都與add/del完全一致 */
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_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
/* 在正常的工作循環中,將通過調用process_events方法來處理事件。
* 這個方法僅在ngx_process_events_and_timers方法中調用,它是處理分發事件的核心。*/
ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
void (*done)(ngx_cycle_t *cycle);
/* 初始化和退出事件驅動模塊的方法 */
} ngx_event_actions_t;
extern ngx_event_actions_t ngx_event_actions;
作爲nginx中比較重要的一個驅動,event當然是很複雜的,第一遍看反正怎麼也不容易懂,後續刨析了nginx的各種機制和流程應該就很容易懂了,先放過它,繼續往下看。