1. 程式人生 > >Nginx的主要函式呼叫關係分析

Nginx的主要函式呼叫關係分析

感覺上次寫的:

http://blog.csdn.net/houjixin/article/details/59658022

這篇總結沒把Nginx最主要的函式呼叫關係說清楚,因此,這次又重新組織了一下,以更簡單的方式描述Nginx中如何從main函式開始,到建立各個程序,然後當有客戶端連入進來時如何進行訊息讀取,這篇總結還未涉及到對業務流程的處理,例如對http請求的處理等。

Nginx支援epoll、select、kqueue等不同作業系統下的各種IO多路複用方式,本文以epoll為例分析Nginx是如何實現在一套處理流程中同時相容各種不同的IO多路複用方式?關於IO多路複用相關的描述可參考:http://blog.csdn.net/houjixin/article/details/27662489

幾乎所有的伺服器程式的工作模式都是:

l     初始化一些引數;

l     開啟監聽socket;

l     在主執行緒的死迴圈(一般都是死迴圈)中對監聽socket和業務socket進行IO狀態監控(IO多路複用),並對IO進行處理;

Nginx也不例外,它的主要工作模式是多程序模式,那麼它的工作方式也是類似的。只不過是它的死迴圈被分散到了各個工作程序中。這裡將按照一般伺服器程式的工作順序從main函式切入,逐步深入,並對各個流程進行分析。

mian函式入口的主流程呼叫關係如下圖所示:


主流程的函式執行順序

各函式的執行順序說明:

(1)Main函式在檔案:src\core\nginx.c中;

(2)核心資料結構 ngx_cycle_t(ngx_cycle_s)儲存了Nginx的核心資料結構,包括記憶體池、日誌等資訊,nginx幾乎所有的操作都圍繞該資料結構進行;

(3)開啟監聽socket

函式呼叫關係

 main=> ngx_init_cycle=>ngx_open_listening_sockets

(4)建立工作程序

在main函式的最後,呼叫了主迴圈處函式:ngx_master_process_cycle或ngx_single_process_cycle中,

(5)主處理流程:ngx_master_process_cycle,在該函式中又呼叫了ngx_start_worker_processes函式來建立並啟動工作程序;

(6)在ngx_start_worker_processes中,在該函式中呼叫了ngx_spawn_process建立工作程序,工作程序所執行的程式碼為處理函式ngx_worker_process_cycle;

(7)工作程序主處理函式:ngx_worker_process_cycle,在該函式中死迴圈呼叫:ngx_process_events_and_timers進行事件處理;

(8)在函式ngx_process_events_and_timers中,呼叫了巨集ngx_process_events對事件進行處理,需要特別注意該巨集, nginx通過它實現了對不同作業系統下各種IO複用方式進行統一處理,這一過程將會在下面詳細描述。

(9)巨集ngx_process_events被定義為:ngx_event_actions.process_events;其中全域性變數ngx_event_actions裡儲存了處理事件的所有函式指標,ngx_event_action在檔案ngx_event.c中被定義為ngx_event_actions_t型別。

(10)ngx_event_actions的型別為:ngx_event_actions_t的全域性變數,定義在檔案ngx_event.c中,型別ngx_event_actions_t定義形式為:

typedefstruct {

    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);

    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);

    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 (*notify)(ngx_event_handler_pt handler);

    ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,

                   ngx_uint_t flags);

    ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);

    void      (*done)(ngx_cycle_t *cycle);

} ngx_event_actions_t;

這樣在Nginx中,全域性變數ngx_event_actions裡儲存了抽象出來的add、del、enable等事件操作函式。

(3)對於Nginx所支援的每種IO複用型別,例如:epoll、poll、kqueue等,它們都會定義自己的處理函式;各IO複用模式的操作函式指標被放在結構體型別ngx_event_module_t中,其定義為:

typedefstruct {

    ngx_str_t              *name;

    void                 *(*create_conf)(ngx_cycle_t*cycle);

    char                 *(*init_conf)(ngx_cycle_t*cycle, void *conf);

 ngx_event_actions_t     actions;

}ngx_event_module_t;

(4)對於epoll,它在檔案ngx_epoll_module.c定義了自己的結構體變數:

ngx_event_module_t ngx_epoll_module_ctx

在定義的同時,也給成員ngx_event_actions_tactions賦值了全部的事件操作函式指標,如下圖所示:

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 */

#if(NGX_HAVE_EVENTFD)

        ngx_epoll_notify,                /* trigger a notify */

#else

        NULL,                            /* trigger a notify */

#endif

        ngx_epoll_process_events,        /* process the events */

        ngx_epoll_init,                  /* init the events */

        ngx_epoll_done,                  /* done the events */

    }

};

可以看到諸如處理事件的函式指標ngx_epoll_process_events等等一系列的事件操作函式指標;

(5)在epoll的ngx_epoll_init函式中將所定義epoll的全部處理函式指標ngx_epoll_module_ctx.actions賦值給該全域性變數ngx_event_actions,如下所示:

ngx_event_actions= ngx_epoll_module_ctx.actions;

從而,我們前面重點關注的巨集ngx_process_events也就被賦予了操作epoll的具體函式指標。

(6)以事件處理函式指標ngx_epoll_process_events為例,可以看到,在該函式中:

l  呼叫epoll_wait等待epoll的讀事件的到來;

l  Epoll_wait返回之後,通過一個for迴圈處理所有就緒的socket;

l  如果socket有資料要讀,則執行:rev->handler(rev)進行資料讀取;如果有socket有資料要寫,則執行:wev->handler(wev)進行資料寫入;