四、nginx啟動過程中的進程創建(參考《深入剖析Nginx》)

  1. 通過前文(《nginx的函數調用》),已知nginx啟動時有如下順序:

main --> ngx_master_process_cycle --> ngx_start_worker_processes

大致是先啟動主進程,再通過ngx_start_worker_processes啟動子進程。在main函數末尾,有如下代碼:

 if (ngx_process == NGX_PROCESS_SINGLE) {
    ngx_single_process_cycle(cycle);
} else {
    ngx_master_process_cycle(cycle);
}

從本段代碼看,如果用戶沒有配置單進程運行的話,就會進入ngx_master_process_cycle()函數。該函數的參數cycle是一個ngx_cycle_s結構體,存儲著許多nginx運行需要的全局信息,但在本節不是重點專注的范圍。

2.下面看一看ngx_master_process_cycle()函數的內容。該函數位于 ngx_process_cycle.c中,其中有如下片段:

ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);       
ngx_start_worker_processes(cycle, ccf->worker_processes,NGX_PROCESS_RESPAWN);
ngx_start_cache_manager_processes(cycle, 0);

第一行代碼估計是獲取配置信息,第二行代碼便是上文nginx啟動順序中的ngx_start_worker_processes函數。該函數有三個參數,第一個參數是ngx_cycle_s結構體,第二個參數用于指示要創建的工作進程的個數,第三個參數被定義為:
“#define NGX_PROCESS_RESPAWN -3”
這個參數大概是用來定義進程意外終止后是否重啟的一個常量,此處不屬于重點關注的內容。

3.關于ngx_start_worker_processes()函數。該函數也位于ngx_process_cycle.c中。其定義很簡潔:

static void
ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
{
ngx_int_t      i;
ngx_channel_t  ch;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes");
ch.command = NGX_CMD_OPEN_CHANNEL;
for (i = 0; i < n; i++) {
    cpu_affinity = ngx_get_cpu_affinity(i);
    ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL,
                      "worker process", type);
    ch.pid = ngx_processes[ngx_process_slot].pid;
    ch.slot = ngx_process_slot;
    ch.fd = ngx_processes[ngx_process_slot].channel[0];
    ngx_pass_open_channel(cycle, &ch);
    }
}

在這個函數中,

  • ngx_int_t被定義為:”typedef intptr_t ngx_int_t”。也就是說,ngx_int_t也就是intptr_t類型,而intptr_t存在的意義跨平臺,其長度總是所在平臺的位數,作用是用來存放地址。

  • ngx_channel_t 定義為

    typedef struct {
     ngx_uint_t  command;
     ngx_pid_t   pid;
     ngx_int_t   slot;
     ngx_fd_t    fd;
    } ngx_channel_t;
    

    這是nginx用于進程間通信準備的結構體。

  • for循環n次,創建n個子進程,也就是參數ccf->worker_processes個子進程。而具體fork產生子進程的代碼,位于循環中的ngx_spawn_process方法中。

  1. 接下來就是子進程的創建了。ngx_spawn_process函數也位于ngx_process_cycle.c中,共五個參數。其函數頭定義為:

    ngx_pid_t
    ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
    char *name, ngx_int_t respawn)
    

    其中第二個參數是函數指針,這里傳入了一個用于處理子進程的函數ngx_worker_process_cycle。現在先看看ngx_spawn_process函數中創建子進程的代碼:

     pid = fork();
     switch (pid) {
     case -1:
     ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                  "fork() failed while spawning \"%s\"", name);
     ngx_close_channel(ngx_processes[s].channel, cycle->log);
     return NGX_INVALID_PID;
     case 0:
        ngx_pid = ngx_getpid();
        proc(cycle, data);
        break;
     default:
        break;
    

    定義很清晰,使用fork產生子進程:

    • 若出錯則處理錯誤。
    • 若為父進程則直接退出switch語句,繼續往下運行,直到ngx_spawn_process末尾,將子函數的pid返回。
    • 若為子進程,則先獲取pid,然后執行 proc(cycle, data),而proc就是上文中傳入ngx_spawn_process的函數指針ngx_worker_process_cycle。

5.至此,父子進程開始同時運行。

  • 下面先看看ngx_worker_process_cycle函數中子進程的運行模式,下面ngx_worker_process_cycle函數的大致結構:

     ngx_worker_process_init(cycle, 1);
     #if (NGX_THREADS){
     ...
     }
    #endif
     for ( ;; ) {
      ...//事件處理
     }  
    

    在ngx_worker_process_cycle函數中,先執行了子進程的初始化函數,之后會進入無限for循環,在for循環中進行工作。

  • 而在父進程中,將會結束ngx_spawn_process,返回子進程的pid,回到ngx_start_worker_processes函數的for循環中,也就是

    for (i = 0; i < n; i++) {
      cpu_affinity = ngx_get_cpu_affinity(i);
      ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL,
                        "worker process", type);
      ch.pid = ngx_processes[ngx_process_slot].pid;
      ch.slot = ngx_process_slot;
      ch.fd = ngx_processes[ngx_process_slot].channel[0];
      ngx_pass_open_channel(cycle, &ch);
    }
    

    父進程在對子進程的信息做一下記錄和處理后,又會進行下一次循環,產生新的子進程,直到產生的子進程數量達到參數ccf->worker_processes個。

6.當所有子進程創建完畢后,父進程將結束ngx_start_worker_processes,回到ngx_master_process_cycle函數中,也就是如下代碼段中繼續運行:

ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);       
ngx_start_worker_processes(cycle, ccf->worker_processes,NGX_PROCESS_RESPAWN);
ngx_start_cache_manager_processes(cycle, 0);
...
for ( ;; ) {
...//信號處理
}

顯然,父進程也進入了無限for循環,在循環中工作。

7.總結一下nginx啟動時的進程創建過程。從調用函數的順序上看,若配置為只產生一個工作進程,則大致如下:


一個主進程,一個工作進程
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容