nginx master 進程主流程
之前有說到 nginx 進程模型-整體架構,下面來看一下 nginx master
進程的主要工作
nginx
的入口 main
函數在 nginx.c
文件中
函數原型為:
int ngx_cdecl
main(int argc, char *const *argv)
在這個函數中,master
做了一系列的初始化操作
最終在下面這個地方進入了主流程中:
//...
if (ngx_process == NGX_PROCESS_SINGLE) {
ngx_single_process_cycle(cycle);
} else {
ngx_master_process_cycle(cycle);
}
//...
因為我們主要看 master-worker
這種進程模型,所以進入 ngx_master_process_cycle
設置信號屏蔽字,防止創建子進程過程中被信號中斷
// 先清空信號集
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigaddset(&set, SIGALRM);
sigaddset(&set, SIGIO);
sigaddset(&set, SIGINT);
sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));
// 設置信號屏蔽字,將 set 中的信號設置為阻塞狀態,防止創建worker 的過程中,被進來的信號打斷
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigprocmask() failed");
}
// 將 set 清空
sigemptyset(&set);
設置信號屏蔽字,防止在創建子進程的過程中被信號處理程序中斷
關於信號屏蔽字,引用 《UNIX 環境高級編程》
中信號一節的部分內容:
進程可以選用
“阻塞信號遞送”
。如果為進程產生了一個阻塞
的信號,而且對該信號的動作是系統默認動作或捕捉該信號, 則為該進程將此信號保持為未決狀態
,直到該進程對此信號解除了阻塞, 或者將對此信號的動作更改為忽略。內核在遞送
一個原來被阻塞的信號給進程時(而不是在產生該信號時),才決定對它的處理方式。於是進程在信號遞送給它之前仍可改變對該信號的動作。進程調用sigpending
函數(見10.13節)來判定哪些信號是設置為阻塞並處於未決狀態的。
每個進程都有一個
信號屏蔽字( signal mask)
,它規定了當前要阻塞遞送到該進程的信號集
。對於每種可能的信號,該屏蔽字中都有一位與之對應。對於某種信號,若其對應位已設置,則它當前是被阻塞的。進程可以調用sigprocmask
(在10.12節中說明)來檢測和更改其當前信號屏蔽字。
當然,在下面創建完子進程之后,會使用 sigsuspend
解除信號屏蔽,並使 master 進程進入休眠
關於 sigsuspend
函數,簡單來說,它是一個 sigprocmask(SIG_SETMASK, &emptyset, NULL)
和 pause()
函數的結合體,不過相對於使用兩個函數完成上述操作,sigsuspend
是 原子操作
。
具體的操作如下:
- 使用新的信號集合設置屏蔽字,在這里是清空屏蔽字
- 調用信號處理函數,並從信號處理程序返回
- 屏蔽字恢復成調用
sigsuspend
之前的值(再次不讓進程被信號打斷)
設置 master 進程的 title
static u_char master_process[] = "master process";
size = sizeof(master_process);
for (i = 0; i < ngx_argc; i++) {
size += ngx_strlen(ngx_argv[i]) + 1;
}
title = ngx_pnalloc(cycle->pool, size);
if (title == NULL) {
/* fatal */
exit(2);
}
p = ngx_cpymem(title, master_process, sizeof(master_process) - 1);
for (i = 0; i < ngx_argc; i++) {
*p++ = ' ';
p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size);
}
ngx_setproctitle(title);
將進程title
設置成三個部分:
- 固定字符:
nginx:
- 主進程標志:
master process
- 命令行啟動的命令,如:
/home/ubuntu/mydisk/var/nginx-debug-1/sbin/nginx
所以進程剛開始是這樣顯示的:
$ ps aux | grep nginx | grep -v grep
root 183117 0.0 0.0 4324 2680 ? ts 17:07 0:00 /home/ubuntu/mydisk/var/nginx-debug-1/sbin/nginx
設置了 title 之后,變成了下面這樣:
$ ps aux | grep nginx | grep -v grep
root 183117 0.0 0.0 4324 2680 ? ts 17:07 0:00 nginx: master process /home/ubuntu/mydisk/var/nginx-debug-1/sbin/nginx
根據配置啟動相應數量的 worker
和 cache
管理進程
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);
監聽信號,並作出響應
在 ngx_init_signals
函數中,對原始信號做了變量名映射,具體映射如下:
信號 | 對應進程中的標志位變量 | 含義 |
---|---|---|
QUIT | ngx_quit | 優雅關閉服務 |
TERM 或 INT | ngx_terminate | 強制關閉服務 |
USR1 | ngx_reopen | 重新打開服務中的所有文件 |
WINCH | ngx_noaccept | 所有子進程不再接受處理新的連接,實際相當於對所有的子進程發送 QUIT 信號 |
USR2 | ngx_change_binary | 平滑升級到新版本的 Nginx 程序 |
HUP | ngx_reconfigure | 重新讀取配置文件並使服務對新配置項生效 |
CHLD | ngx_reap | 有子進程意外結束 master 會監控所有子進程,並在子進程意外退出時調用 ngx_reap_children 方法重啟子進程 |
master 並不是時刻不停的執行循環檢測這些標志位,而是通過 sigsuspend
進入休眠,等待有信號喚醒進程時,再循環檢測所有信號並處理。
參考
《UNIX 環境高級編程》