- 通過(guò)前文(《nginx的函數(shù)調(diào)用》),已知nginx啟動(dòng)時(shí)有如下順序:
main --> ngx_master_process_cycle --> ngx_start_worker_processes
大致是先啟動(dòng)主進(jìn)程,再通過(guò)ngx_start_worker_processes啟動(dòng)子進(jìn)程。在main函數(shù)末尾伙判,有如下代碼:
if (ngx_process == NGX_PROCESS_SINGLE) {
ngx_single_process_cycle(cycle);
} else {
ngx_master_process_cycle(cycle);
}
從本段代碼看,如果用戶(hù)沒(méi)有配置單進(jìn)程運(yùn)行的話(huà)值漫,就會(huì)進(jìn)入ngx_master_process_cycle()函數(shù)澳腹。該函數(shù)的參數(shù)cycle是一個(gè)ngx_cycle_s結(jié)構(gòu)體织盼,存儲(chǔ)著許多nginx運(yùn)行需要的全局信息杨何,但在本節(jié)不是重點(diǎn)專(zhuān)注的范圍。
2.下面看一看ngx_master_process_cycle()函數(shù)的內(nèi)容沥邻。該函數(shù)位于 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);
第一行代碼估計(jì)是獲取配置信息,第二行代碼便是上文nginx啟動(dòng)順序中的ngx_start_worker_processes函數(shù)唐全。該函數(shù)有三個(gè)參數(shù)埃跷,第一個(gè)參數(shù)是ngx_cycle_s結(jié)構(gòu)體,第二個(gè)參數(shù)用于指示要?jiǎng)?chuàng)建的工作進(jìn)程的個(gè)數(shù)邮利,第三個(gè)參數(shù)被定義為:
“#define NGX_PROCESS_RESPAWN -3”
這個(gè)參數(shù)大概是用來(lái)定義進(jìn)程意外終止后是否重啟的一個(gè)常量弥雹,此處不屬于重點(diǎn)關(guān)注的內(nèi)容。
3.關(guān)于ngx_start_worker_processes()函數(shù)延届。該函數(shù)也位于ngx_process_cycle.c中剪勿。其定義很簡(jiǎn)潔:
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);
}
}
在這個(gè)函數(shù)中,
ngx_int_t被定義為:”typedef intptr_t ngx_int_t”方庭。也就是說(shuō)厕吉,ngx_int_t也就是intptr_t類(lèi)型,而intptr_t存在的意義跨平臺(tái)械念,其長(zhǎng)度總是所在平臺(tái)的位數(shù)头朱,作用是用來(lái)存放地址。
-
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用于進(jìn)程間通信準(zhǔn)備的結(jié)構(gòu)體龄减。
for循環(huán)n次项钮,創(chuàng)建n個(gè)子進(jìn)程,也就是參數(shù)ccf->worker_processes個(gè)子進(jìn)程。而具體fork產(chǎn)生子進(jìn)程的代碼烁巫,位于循環(huán)中的ngx_spawn_process方法中鳖敷。
-
接下來(lái)就是子進(jìn)程的創(chuàng)建了。ngx_spawn_process函數(shù)也位于ngx_process_cycle.c中程拭,共五個(gè)參數(shù)定踱。其函數(shù)頭定義為:
ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn)
其中第二個(gè)參數(shù)是函數(shù)指針,這里傳入了一個(gè)用于處理子進(jìn)程的函數(shù)ngx_worker_process_cycle∈研現(xiàn)在先看看ngx_spawn_process函數(shù)中創(chuàng)建子進(jìn)程的代碼:
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產(chǎn)生子進(jìn)程:
- 若出錯(cuò)則處理錯(cuò)誤。
- 若為父進(jìn)程則直接退出switch語(yǔ)句恤浪,繼續(xù)往下運(yùn)行畅哑,直到ngx_spawn_process末尾,將子函數(shù)的pid返回水由。
- 若為子進(jìn)程荠呐,則先獲取pid,然后執(zhí)行 proc(cycle, data),而proc就是上文中傳入ngx_spawn_process的函數(shù)指針ngx_worker_process_cycle砂客。
5.至此泥张,父子進(jìn)程開(kāi)始同時(shí)運(yùn)行。
-
下面先看看ngx_worker_process_cycle函數(shù)中子進(jìn)程的運(yùn)行模式鞠值,下面ngx_worker_process_cycle函數(shù)的大致結(jié)構(gòu):
ngx_worker_process_init(cycle, 1); #if (NGX_THREADS){ ... } #endif for ( ;; ) { ...//事件處理 }
在ngx_worker_process_cycle函數(shù)中媚创,先執(zhí)行了子進(jìn)程的初始化函數(shù),之后會(huì)進(jìn)入無(wú)限for循環(huán)彤恶,在for循環(huán)中進(jìn)行工作钞钙。
-
而在父進(jìn)程中,將會(huì)結(jié)束ngx_spawn_process声离,返回子進(jìn)程的pid芒炼,回到ngx_start_worker_processes函數(shù)的for循環(huán)中,也就是
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); }
父進(jìn)程在對(duì)子進(jìn)程的信息做一下記錄和處理后术徊,又會(huì)進(jìn)行下一次循環(huán)本刽,產(chǎn)生新的子進(jìn)程,直到產(chǎn)生的子進(jìn)程數(shù)量達(dá)到參數(shù)ccf->worker_processes個(gè)弧关。
6.當(dāng)所有子進(jìn)程創(chuàng)建完畢后盅安,父進(jìn)程將結(jié)束ngx_start_worker_processes,回到ngx_master_process_cycle函數(shù)中世囊,也就是如下代碼段中繼續(xù)運(yùn)行:
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 ( ;; ) {
...//信號(hào)處理
}
顯然别瞭,父進(jìn)程也進(jìn)入了無(wú)限for循環(huán),在循環(huán)中工作株憾。
7.總結(jié)一下nginx啟動(dòng)時(shí)的進(jìn)程創(chuàng)建過(guò)程蝙寨。從調(diào)用函數(shù)的順序上看晒衩,若配置為只產(chǎn)生一個(gè)工作進(jìn)程,則大致如下: