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);
    }
}

 

你可能感兴趣的:(C++,nginx)