nginx master 進程主流程


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原子操作

具體的操作如下:

  1. 使用新的信號集合設置屏蔽字,在這里是清空屏蔽字
  2. 調用信號處理函數,並從信號處理程序返回
  3. 屏蔽字恢復成調用 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設置成三個部分:

  1. 固定字符:nginx:
  2. 主進程標志: master process
  3. 命令行啟動的命令,如:/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

根據配置啟動相應數量的 workercache 管理進程

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 進入休眠,等待有信號喚醒進程時,再循環檢測所有信號並處理。

參考

nginx中的ngx_cdecl

Nginx源碼|Nginx信號處理

nginx 信號處理

LINUX C中sigprocmask()函數用法

linux信號的阻塞和未決

《UNIX 環境高級編程》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM