nginx啟動期做了哪些事

2021-06-02 21:33:54 字數 3927 閱讀 7864

nginx是個多程序web容器,不同的配置下它的啟動方式也是不同的,這裡我只說說最典型的啟動方式。

它有1個master程序,和多個worker程序(最優配置的數量與cpu核數相關)。那麼,首先我們要找到main函式,它在src/core/nginx.c檔案中。談到原始碼了,這時我們先簡單看下原始碼的目錄結構吧。

nginx主要有下列目錄:

src/core,這個目錄存放了基礎的資料結構像list、紅黑樹、nginx字串,貫穿始終的一些邏輯結構如ngx_cycle_s、ngx_connection_s等,還有對一些底層操作的封裝如log、檔案操作、共享記憶體、記憶體池等,最後還有個nginx.c這個main啟動函式了。

src/event,這個目錄下存放與抽象事件相關的結構和鉤子函式。nginx是以事件驅動處理流程的,事件自然是整個體系的核心了,這裡定義了最核心的ngx_event_s結構。

src/event/modules目錄存放了具體的種種事件驅動方式,例如epoll、kqueue、poll、aio、select等,它們通過ngx_event_actions_t結構體中的鉤子掛在nginx中。nginx啟動時會根據配置來決定使用哪種實現方式。

src/os/unix中存放了unix系統下許多函式呼叫的unix實現。

src/http目錄存放到http module的相關實現,這個module負責處理http請求,包括協議的解析以及訪問backend server的**。

src/http/module目錄存放http module型別的一些特定用途的module,比如gzip處理加密,壓縮等。

有個初步了解後,回到main函式中,順序看看我們感興趣的事情。它先執行了ngx_time_init,為什麼要初始化時間呢?nginx考慮的還是很周到的,取系統時間gettimeofday是系統呼叫,這意味著,需要傳送中斷給linux核心,核心需要做程序間切換來處理這個呼叫。這是乙個不能忽視成本的函式。nginx封裝了時間函式,這樣,每次我們需要處理時間時,並不是呼叫gettimeofday,而是nginx自己快取的時間,這樣大量減少了系統呼叫,取當前時間這事可是誰都愛幹的。

那麼,nginx是怎麼維護自己的這個時鐘呢?如何保證使用者取到的當前時間是有意義的?nginx設計者的出發點是,nginx是事件驅動機制,當一批事件發生時,也就是epoll_wait返回時,會取一次gettimeofday來更新自己的時間,然後呼叫各個事件對應的處理函式。這些函式都會保證自己是無阻塞的,也就是毫秒級的處理能力,所以,在任何乙個事件處理函式中,取到的時間都是之前epoll_wait剛返回時取到的時間,這樣,即使拿到的時間慢了幾毫秒也無所謂。關鍵是,每個函式都是無阻塞的,都要迅速的把控制權交還給nginx,這是基本設計原則哈。

main函式初始化時間後,建立了最核心的資料結構ngx_cycle,之後無論是worker程序還是master程序都是圍繞著它進行的。下面,我們要超級關注ngx_init_cycle這個函式,啟動過程中大量的工作是在這完成的,**就不列了,這個函式有800行,超大,也可見其之關鍵。ngx_init_cycle裡做的第一件事就是呼叫所有nginx module裡的create_conf方法。好,現在我們才來詳細看下nginx module是什麼。

ngx_module_t *ngx_modules =

這個通過configure生成的全域性變數很關鍵,只有它才知道,乙個請求可能會用哪些module處理。

接上文,ngx_init_cycle就是通過ngx_modules陣列來呼叫所有module的create_conf方法的(每個module有權力決定是否實現這個方法,如果不實現的話,當然不會呼叫了)。然後,開始處理配置檔案,這裡我們需要重點關注ngx_conf_parse函式,因為它裡面呼叫了ngx_conf_handler方法,ngx_conf_handler方法會呼叫每個module裡自己實現的set鉤子函式,讓每個module處理自己感興趣的配置項。所以,如果你在nginx.conf裡沒有配置某個module想要的東東,這個module雖然編譯進去了,卻會一直不執行的。這裡我們要看下module的結構了,不能總是乾說哈。

struct ngx_module_s ;

ctx_index用來表示我們定義的乙個module在上下文陣列中的序號,index就表示在ngx_modules這個陣列中的序號。

ctx這個指標指向module的上下文,type表示這個module的型別(module是分類的,每種type可以有多個module的),下面8個鉤子函式,表示對應的事件發生時會呼叫這些方法(當然,module也可以不實現)。commands指向這個module所屬的command結構,例如,http module是這麼定義自己的command的:

我們再看看ngx_command_s的定義:

struct ngx_command_s ;

所以,上文我說過,ngx_conf_handler方法會呼叫每個module裡自己實現的set鉤子函式,如果我們編譯了http module(預設都有),那麼就會在ngx_conf_handler方法中呼叫上面的ngx_http_block函式。這個ngx_http_block函式值得詳細說說,因為它這時讀取配置檔案,決定要監聽哪些http埠,它會把這些資訊通過傳進來的ngx_conf_t指標塞給ngx_cycle這個核心變數。

ngx_event_core_module也是個核心module,之前說到的到底是由epoll、select、poll還是kqueue來實現io多路復用,就是由這個module來搞定的。

繼續向下。ngx_init_cycle函式再來呼叫所有module實現的init_conf鉤子函式。之後,執行到現在nginx程序終於要開始監聽埠了。這事很關鍵,剛剛呼叫過各個module的set鉤子方法了,例如上面http module的ngx_http_block方法,這些方法已經給ngx_cycle的listening陣列塞進了需要監聽的埠。為什麼要現在就開始listen呢?因為現在還沒有fork出worker子程序哈。大家知道,linux系統下,fork出的子程序會共享父程序的位址空間,所以,需要在全部worker程序中做的事,就都放到ngx_init_cycle裡來做吧。監聽的控制代碼,會被所有nginx worker共享使用的。

監聽完指定的埠後,開始呼叫所有module實現的init_module鉤子函式。接下來,要準備程序間通訊的事了。乙個master,多個worker,這些程序間通過什麼方式通訊呢?這裡不展開了,下次再細說。它們也通過共享記憶體交換資料,這時開始初始化共享記憶體。終於,ngx_init_cycle執行完了,鬆口氣?

接著,main函式要初始化訊號量,程序間的同步都是通過訊號量來玩的。然後建立pidfile,這個檔案用於啟動完成以後通過nginx命令列,對nginx程序傳送訊號量來控制它。main函式的最後,開始執行ngx_master_process_cycle函式了,這個函式做master程序該做的事。它首先呼叫ngx_start_worker_processes去啟動worker,按照配置檔案中配置的worker數量,fork出許多子程序,每個子程序執行ngx_worker_process_cycle函式,這是個死迴圈函式,將開始處理真正的使用者請求。

ngx_master_process_cycle函式再呼叫ngx_start_cache_manager_processes啟動cache的管理程序,這塊限於篇幅,下次有空再講吧。最後,ngx_master_process_cycle進入死迴圈,開始準備接收訊號量傳來的命令,以及監控每乙個worker的執行狀態,如果有worker非正常死掉,還會重新拉起的。

最後宣告下,我是以nginx-0.7.65版本做例子來說的,上面列到的源**檔案,都是針對這個版本的。當然,我說的這些都是核心函式,其實1.x版本與之差別非常小。

熟悉了nginx的啟動過程,知道它幹了哪些事,就可以研究worker程序如何處理使用者請求了。下次再說吧。

建立物件做了哪些事

class student class studentdemo 問執行類的初始化過程 student s new student 時,在記憶體做了哪些事情?1 把student.class檔案載入到記憶體 2 在棧記憶體為s變數開闢空間 3 在堆記憶體為學生物件申請空間 4 給學生的成員變數進行預設...

new操作符做了哪些事情?

var func function var func new func new共經過了4個階段 1 建立乙個空物件 var obj new object 2 設定原型鏈 obj.proto func.prototype 3 讓func中的this指向obj,並執行func的函式體。var resul...

js的預編譯做了哪些事情

js執行的三部曲 1語法解析 總體掃瞄一遍一遍看一下是不是有低階語法錯誤 2.預編譯 函式執行前會進行預編譯 3.解釋執行 一行一行的進行編譯 我們主要說一下預編譯 預編譯 變數未經宣告就賦值,此變數就為全域性變數 functiona console.log b 20 console.log a 報...