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 ( ;; ) {
// ...
}
}
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);
}
}
创建子进程时,会调用 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;
}
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子进程处理信号、请求流程
}
}
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);
}
}