1. 程式人生 > >nginx原始碼初讀(8)--讓煩惱從資料結構開始(ngx_event)

nginx原始碼初讀(8)--讓煩惱從資料結構開始(ngx_event)

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