三:深入理解Nginx的模組化 (結合原始碼詳解)
盜用前面用到的流程圖
第二步實際上是呼叫
ngx_add_inherited_sockets()
//檔名: Nginx.c
int ngx_cdecl
main(int argc, char *const *argv)
{
...
if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {
return 1;
}
...
}
Nginx在不重啟服務升級,舊版本的
master
程序會通過execve
系統呼叫來啟動新版本的master
程序(先 fork()出子程序再呼叫exec來執行新程式),這時舊版本 的master
程序通過 環境變數通知新版本的master
這是在升級,新版本的master
程序通過ngx_add_inherited_sockets()
方法 由環境變數裡讀取平滑升級資訊,並對舊版本Nginx服務監聽 的控制代碼做繼承處理 。
3~8步都在
ngx_init_cycle()
方法中執行的。在初始化ngx_cycle_t
中所有的容器後,會為讀取、解析檔案 做準備工作。因為每個 模組都必須有相應的資料結構來 儲存配置檔案中的各配置項,建立這些資料結構的工作都需要在這一步進行。Nginx框架只關心NGX_CORE_MODULE
核心模組,從而降低框架的複雜度,這裡將會呼叫所有核心模組的create_conf
的方法(也只有核心模組才有這個方法 ),這意味著所有核心模組開始構造用於儲存配置項的結構體。非核心模組將由每個模組進行管理,如 HTTP模組都由ngx_http_module
管理。這樣ngx_http_module
在解析自己感興趣的”http”配置項時,將會呼叫所有HTTP模組 約定的方法來 建立儲存配置的結構體
//檔名 ngx_cycle.h
typedef struct ngx_cycle_s ngx_cycle_t;
struct ngx_cycle_s {
void ****conf_ctx; // 配置上下文陣列(含所有模組)
ngx_pool_t *pool; // 記憶體池
ngx_log_t *log; // 日誌
ngx_log_t new_log;
ngx_connection_t **files; // 連線檔案
ngx_connection_t *free_connections; // 空閒連線
ngx_uint_t free_connection_n; // 空閒連線個數
ngx_module_t **modules;
ngx_uint_t modules_n;
ngx_uint_t modules_used; /* unsigned modules_used:1; */
ngx_queue_t reusable_connections_queue; // 再利用連線佇列
ngx_array_t listening; // 監聽套接字陣列
ngx_array_t pathes; // 路徑陣列
ngx_list_t open_files; // 開啟檔案連結串列
ngx_list_t shared_memory; // 共享記憶體連結串列
ngx_uint_t connection_n; // 連線個數
ngx_uint_t files_n; // 開啟檔案個數
ngx_connection_t *connections; // 連線
ngx_event_t *read_events; // 讀事件
ngx_event_t *write_events; // 寫事件
ngx_cycle_t *old_cycle; // old cycle指標
ngx_str_t conf_file; // 配置檔案
ngx_str_t conf_param; // 配置引數
ngx_str_t conf_prefix; // 配置檔案目錄
ngx_str_t prefix; // 程式工作目錄
ngx_str_t lock_file; // 鎖檔案,用在不支援accept_mutex的系統中
ngx_str_t hostname; // 主機名
};
呼叫配置模組提供的解析配置項方法 。遍歷
nginx.conf
的所有配置項,對於任一配置項,將會檢查所有核心模組以找出對它所感興趣的模組。並呼叫該模組在ngx_command_t
結構體中的定義的 配置項處理辦法 。
呼叫所有的NGX_CORE_MODULE
核心模組的init_conf
的 方法。這一步驟的 目的在於讓所有核心模組在解析完配置項可以做綜合性處理。
之前第四步在 解析配置項時,所有的模組都已經解析出自己需要監聽的埠 ,如HTTP模組 已經 在 解析http{…}配置項時得到要監聽的埠 ,並新增到listening陣列中。這一步驟就是按照listening陣列中的每一個ngx_listening_t
元素設定socket控制代碼並監聽埠,實際上就是呼叫ngx_open_listening_sockets()
。
在這個階段會呼叫所有模組的init_module
方法。接下來 就是根據 配置Nginx執行模式決定如何工作。
接下來流程可以參考之前的部落格nginx程式碼分析
worker程序工作流程
master
採用的是訊號的方式通知worker程序停止服務或更換日誌。在函式ngx_worker_process_cycle()
通過檢查ngx_exiting、ngx_terminate、ngx_quit、ngx_reopen
這4個標誌位來決定後續動作。
master程序工作流程
master
程序不需要處理網路事件,不負責業務的執行,只會通過該管理worker
等子程序來實現重啟服務、平滑升級、更換日誌檔案、配置檔案實時生效等 功能 。
master
程序中所有子程序相關的狀態資訊都儲存在ngx_processes陣列中,下面 是陣列元素的型別ngx_process_t
的 結構的定義,程式碼如下 :
typedef struct {
//程序 ID
ngx_pid_t pid;
// 由waitpid系統呼叫獲取到程序狀態
int status;
// 這是由socketpair系統呼叫產生出用於程序 間通訊 的socket控制代碼
ngx_socket_t channel[2];
// 子程序的迴圈執行的辦法,當父程序呼叫ngx_spawn_process 生成子程序時使用
ngx_spawn_proc_pt proc;
void *data;
// 程序名稱。作業系統中顯示的程序名稱與name相同
char *name;
// 標誌位,為1時表示在重新生成子程序
unsigned respawn:1;
// 標誌位, 為1時表示正在 生成子程序
unsigned just_spawn:1;
// 標誌位,為1時表示在父程序、子程序分離
unsigned detached:1;
// 標誌位,為1時表示程序正在退出
unsigned exiting:1;
// 標誌位,為1時表示程序已經退出
unsigned exited:1;
} ngx_process_t;
ngx_spawn_process
方法封裝了fork
系統呼叫,並且會從ngx_processes
陣列中選擇一個還未使用的ngx_process_t
元素儲存這個子程序的相關資訊。