/* 入参介绍:
* cycle: ngx_cycle_t结构体指针,不解释了,《笔记十二》
* proc:创建子进程的回调函数,worker进程从此入口开始执行,后续的worker工作在此接口中
* data: worker进程proc回调函数的入参
* name: worker进程名字
* respawn: 见上面进程属性介绍
*/
ngx_pid_t
ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
char *name, ngx_int_t respawn)
{
u_long on;
ngx_pid_t pid;
ngx_int_t s; // 后面代码你可以看出,s即为创建进程在全部进程数组中的下标
if (respawn >= 0) {
s = respawn; // 如果respawn不小于0,则视为当前进程已经退出,需要重启
} else {
/* 寻找一个可创建进程的在全部进程数组中的下标位置 */
for (s = 0; s < ngx_last_process; s++) {
if (ngx_processes[s].pid == -1) {
break;
}
}
/* 如果下标超出Nginx全部进程数组中规定的最大数量,告警并返回错误 */
if (s == NGX_MAX_PROCESSES) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"no more than %d processes can be spawned",
NGX_MAX_PROCESSES);
return NGX_INVALID_PID;
}
}
if (respawn != NGX_PROCESS_DETACHED) {
/* 不是热代码替换 */
/* Solaris 9 still has no AF_LOCAL */
/* 这里相当于Master进程调用socketpair()为新的worker进程创建一对全双工的socket */
if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)
{
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"socketpair() failed while spawning \"%s\"", name);
return NGX_INVALID_PID;
}
ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
"channel %d:%d",
ngx_processes[s].channel[0],
ngx_processes[s].channel[1]);
/* 设置master的channel[0](即写端口),channel[1](即读端口)均为非阻塞方式 */
if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
ngx_nonblocking_n " failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
ngx_nonblocking_n " failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
/* 设置异步模式:
* 这里可以看下《网络编程卷一》的ioctl函数和fcntl函数 or 网上查询
*/
on = 1; // 标记位,ioctl用于清除(0)或设置(非0)操作
/* 设置channel[0]的信号驱动异步I/O标志
* FIOASYNC:该状态标志决定是否收取针对socket的异步I/O信号(SIGIO)
* 其与O_ASYNC文件状态标志等效,可通过fcntl的F_SETFL命令设置or清除
*/
if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"ioctl(FIOASYNC) failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
/* F_SETOWN:用于指定接收SIGIO和SIGURG信号的socket属主(进程ID或进程组ID)
* 这里意思是指定Master进程接收SIGIO和SIGURG信号
* SIGIO信号必须是在socket设置为信号驱动异步I/O才能产生,即上一步操作
* SIGURG信号是在新的带外数据到达socket时产生的
*/
if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fcntl(F_SETOWN) failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
/* FD_CLOEXEC:用来设置文件的close-on-exec状态标准
* 在exec()调用后,close-on-exec标志为0的情况下,此文件不被关闭;非零则在exec()后被关闭
* 默认close-on-exec状态为0,需要通过FD_CLOEXEC设置
* 这里意思是当Master父进程执行了exec()调用后,关闭socket
*/
if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
/* 同上,这里意思是当Worker子进程执行了exec()调用后,关闭socket */
if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
/* 设置当前子进程的socket,Master进程用于监听 */
ngx_channel = ngx_processes[s].channel[1];
} else {
ngx_processes[s].channel[0] = -1;
ngx_processes[s].channel[1] = -1;
}
ngx_process_slot = s; // 这一步将在ngx_pass_open_channel()中用到,就是设置下标,用于寻找本次创建的子进程
pid = fork(); // 不解释了吧
switch (pid) {
case -1:
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fork() failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
case 0:
ngx_pid = ngx_getpid(); // 设置子进程ID
proc(cycle, data); // 调用proc回调函数,即ngx_worker_process_cycle。之后worker子进程从这里开始执行
break;
default:
break;
}
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start %s %P", name, pid);
/* 这一部分用来设置ngx_process_t的成员变量 */
ngx_processes[s].pid = pid;
ngx_processes[s].exited = 0;
/* 如果大于0,则说明是在重启子进程,因此下面的初始化不用再重复做 */
if (respawn >= 0) {
return pid;
}
ngx_processes[s].proc = proc;
ngx_processes[s].data = data;
ngx_processes[s].name = name;
ngx_processes[s].exiting = 0;
/* OK,也不多说了,用来设置状态信息 */
switch (respawn) {
case NGX_PROCESS_NORESPAWN:
ngx_processes[s].respawn = 0;
ngx_processes[s].just_spawn = 0;
ngx_processes[s].detached = 0;
break;
case NGX_PROCESS_JUST_SPAWN:
ngx_processes[s].respawn = 0;
ngx_processes[s].just_spawn = 1;
ngx_processes[s].detached = 0;
break;
case NGX_PROCESS_RESPAWN:
ngx_processes[s].respawn = 1;
ngx_processes[s].just_spawn = 0;
ngx_processes[s].detached = 0;
break;
case NGX_PROCESS_JUST_RESPAWN:
ngx_processes[s].respawn = 1;
ngx_processes[s].just_spawn = 1;
ngx_processes[s].detached = 0;
break;
case NGX_PROCESS_DETACHED:
ngx_processes[s].respawn = 0;
ngx_processes[s].just_spawn = 0;
ngx_processes[s].detached = 1;
break;
}
/* 不用多说了 */
if (s == ngx_last_process) {
ngx_last_process++;
}
return pid;
}
最后,向其他进程广播。
static void
ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch)
{
ngx_int_t i;
/* ngx_last_process全局变量,同样在ngx_spawn_process()中被赋值,意为最后面的进程 */
for (i = 0; i < ngx_last_process; i++) {
// 跳过刚创建的worker子进程 || 不存在的子进程 || 其父进程socket关闭的子进程
if (i == ngx_process_slot
|| ngx_processes[i].pid == -1
|| ngx_processes[i].channel[0] == -1)
{
continue;
}
/* Nginx核心模块的调试日志 */
ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0,
"pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d",
ch->slot, ch->pid, ch->fd,
i, ngx_processes[i].pid,
ngx_processes[i].channel[0]);
/* TODO: NGX_AGAIN */
/* 给每个进程的父进程发送刚创建worker进程的信息,IPC方式以后再搞 */
ngx_write_channel(ngx_processes[i].channel[0],
ch, sizeof(ngx_channel_t), cycle->log);
}
}
总结