nginx worker啓動初始化分析

1、master進程初始化:

nginx是 master-worker多進程模型,程序啓動時首先啓動 master進程,由 master進程根據配置啓動 worker進程,在 master函數中處理代碼如下:

void ngx_master_process_cycle(ngx_cycle_t *cycle) {
    // ..
    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    // 根據配置啓動指定數量的worker進程
    ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN);
    ngx_start_cache_manager_processes(cycle, 0);

    for ( ;; ) {
        // ...
    }
}

 

2、啓動n個worker子進程ngx_start_worker_processes:

ngx_start_worker_processes函數會根據配置參數創建指定數量的 worker子進程,函數首先調用 ngx_spawn_process創建好一個 worker子進程。

因爲一個 master父進程下會有很多個 worker子進程,所以 master父進程每創建一個 worker子進程,都需要把剛創建的 worker子進程信息保存在 master父進程和之前已經創建的所有 worker子進程中,這樣才能保持進程間數據一致性。

所以 master進程每創建一個 worker子進程,都會把當前 worker子進程的信息通過 channel進程間通信告知其他 worker子進程(ngx_pass_open_channel函數)。

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_memzero(&ch, sizeof(ngx_channel_t));

    // 傳遞給其他進程的命令  打開通信通道
    ch.command = NGX_CMD_OPEN_CHANNEL;

    // 創建n個子進程
    for (i = 0; i < n; i++) {
        ngx_spawn_process(cycle, ngx_worker_process_cycle, (void *) (intptr_t) i, "worker process", type);

        // 保存當前worker進程的相關信息  ngx_process_slot是全局的最新worker進程下標
        ch.pid = ngx_processes[ngx_process_slot].pid;
        ch.slot = ngx_process_slot;
        ch.fd = ngx_processes[ngx_process_slot].channel[0];

        // 現在還是master進程  向已經創建的worker子進程廣播 當前創建的worker子進程  讓所有worker子進程保持信息同步
        ngx_pass_open_channel(cycle, &ch);
    }
}

 

3、創建新進程 ngx_spawn_process:

創建子進程時,會調用 ngx_spawn_process函數,函數會根據參數在 ngx_processes數組中找到一個可用的位置存儲新子進程的信息。如果子進程不和父進程分離,會在父子進程間創建 unit socket用於父子進程間通信,目前是父進程發送消息,子進程接收消息。

在 fork子進程後,會執行參數 proc函數,傳入參數 data;如果創建的是 worker子進程,這裏 proc就是 ngx_worker_process_cycle函數。

ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn) {
    // ...
    if (respawn >= 0) {
        // 進程ngx_processes[respawn]  重用該數組內部分進程信息
        s = respawn;

    } else {
        // 在ngx_processes數組中找一個可以使用的位置
        for (s = 0; s < ngx_last_process; s++) {
            if (ngx_processes[s].pid == -1) {
                break;
            }
        }
        if (s == NGX_MAX_PROCESSES) {
            return NGX_INVALID_PID;
        }
    }

    if (respawn != NGX_PROCESS_DETACHED) {
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1) {
            return NGX_INVALID_PID;
        }

        // ... 設置ngx_processes[s].channel[0][1]的socket屬性
        ngx_channel = ngx_processes[s].channel[1];

    } else {
        // 即將生成的子進程爲NGX_PROCESS_DETACHED類型  不需要和master進程通信  所以channel設置爲-1即可
        ngx_processes[s].channel[0] = -1;
        ngx_processes[s].channel[1] = -1;
    }

    ngx_process_slot = s;

    pid = fork();
    switch (pid) {
    case -1:
        return NGX_INVALID_PID;

    case 0:
        // 子進程 調用函數進行處理
        ngx_parent = ngx_pid;
        ngx_pid = ngx_getpid();
        proc(cycle, data);
        break;

    default:
        break;
    }
    
    ngx_processes[s].pid = pid;
    ngx_processes[s].exited = 0;

    // respawn大於0  說明重新該進程  無需設置相關參數
    if (respawn >= 0) {
        return pid;
    }

    // ... 設置ngx_processes[s]進程的相關屬性
    return pid;
}

 

4、子進程處理函數 ngx_worker_process_cycle worker:

fork進程後,worker子進程會進行初始化然後進行循環處理客戶端請求。

static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data) {
    ngx_int_t worker = (intptr_t) data;

    ngx_process = NGX_PROCESS_WORKER;
    ngx_worker = worker;

    ngx_worker_process_init(cycle, worker);
    ngx_setproctitle("worker process");

    for ( ;; ) {
        // ... worker子進程處理信號、請求流程
    }
}

 

5、子進程初始化函數 ngx_worker_process_init:

worker子進程初始化函數主要根據配置設置進程的一些參數。然後調用所有模塊的 init_process初始化進程函數,對所有模塊進行初始化。

因爲 worker子進程創建時繼承了父進程的 全局 ngx_processes數組,所以當前 worker子進程擁有所有 worker子進程的全部信息,這裏關閉除當前 worker子進程外的所有 worker子進程的 channel[0],即關閉其他 worker子進程的監聽socket,保留其他 worker子進程的 channel[1],可以向其他 worker子進程寫入數據。

然後關閉當前 worker子進程的 channel[1],天劍 channel[0]的 epoll監控,等待 master進程向當前 worker子進程發送信號,進行相應處理。

 

這裏當前 worker進程在創建的時候就已經知道其他 worker子進程的信息了(從 master進程繼承過來的)。如果後續再創建新的 worker子進程的話,會在開始創建 worker子進程的 ngx_start_worker_processes函數中,創建完 worker子進程後,把當前 worker子進程的信息通過 channel發送給已經存在的 所有 worker子進程。這樣保證了進程間信息的一致性。

static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker) {
    // ... 根據配置初始化一些基本參數
    for (i = 0; cycle->modules[i]; i++) {
        if (cycle->modules[i]->init_process) {
            if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {
                exit(2);
            }
        }
    }

    // 關閉不使用的socket
    for (n = 0; n < ngx_last_process; n++) {
        if (ngx_processes[n].pid == -1) {
            continue;
        }

        // 跳過當前worker進程
        if (n == ngx_process_slot) {
            continue;
        }

        if (ngx_processes[n].channel[1] == -1) {
            continue;
        }

        // 關閉其他worker進程的channel[1]  保留其他worker進程的channel[0] 可以向其他進程寫數據
        if (close(ngx_processes[n].channel[1]) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "close() channel failed");
        }
    }

    // 關閉當前worker進程的channel[0]  保留當前worker進程的chanel[1] 監聽本進程數據
    if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,"close() channel failed");
    }

    // 監聽當前worker進程的channel[1]可讀事件  worker進程和master進程通信使用
    if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT, ngx_channel_handler) == NGX_ERROR) {
        exit(2);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章