nginx原始碼初讀(8)--讓煩惱從資料結構開始(ngx_event)
阿新 • • 發佈:2018-11-11
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的各種機制和流程應該就很容易懂了,先放過它,繼續往下看。
貼個連結:https://segmentfault.com/a/1190000002715203
--------------------- 本文來自 醇霧 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/wuchunlai_2012/article/details/50731037?utm_source=copy