上一节,我们主要讲了多进程时,主进程如何利用信号量控制子进程的,这一节我们主要讲解下子进程的启动,和执行的操作。
上一节讲过,真正创建worker子进程的函数是ngx_start_worker_processes,这个函数本身很简单:
src/os/unix/ngx_process_cycle.c 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_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes"); //传递给其他worker子进程的命令,打开通信管道 ch.command = NGX_CMD_OPEN_CHANNEL; //创建n个worker子进程 for (i = 0; i < n; i++) { cpu_affinity = ngx_get_cpu_affinity(i); //ngx_spawn_process创建worker子进程并初始化相关资源和属性 //然后执行子进程的执行函数ngx_worker_process_cycle ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL, "worker process", type); //向已经创建的worker进程广播当前创建worker进程信息。。。 ch.pid = ngx_processes[ngx_process_slot].pid; ch.slot = ngx_process_slot; ch.fd = ngx_processes[ngx_process_slot].channel[0]; //接下来详细讲解此方法 ngx_pass_open_channel(cycle, &ch); } } static void ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch) { ngx_int_t i; for (i = 0; i < ngx_last_process; i++) { //跳过自己和异常的worker if (i == ngx_process_slot || ngx_processes[i].pid == -1 || ngx_processes[i].channel[0] == -1) { continue; } 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 ngx_write_channel(ngx_processes[i].channel[0], ch, sizeof(ngx_channel_t), cycle->log); } }
接下来要研究的函数是创建子进程的函数,上面方法中的ngx_spawn_process
src/os/unix/ngx_process.c ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn) { //proc:是子进程的执行函数, //data是参数, //name是子进程的名字 u_long on; ngx_pid_t pid; ngx_int_t s;//将要创建的子进程在进程表中的位置 if (respawn >= 0) { //替换进程ngx_processes[respawn],可安全重用该进程表项 s = respawn; } else { //先找到一个被回收的进程表象 for (s = 0; s < ngx_last_process; s++) { if (ngx_processes[s].pid == -1) { break; } } //进程表已经满 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 */ //创建一对已经连接的无名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]); //设置socket为非阻塞模式 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; } //开启channel[0]的消息驱动IO on = 1; //FIOASYNC:设置/清楚信号驱动异步I/O标志 /*根据iocl 的第三个参数指向一个0 值或非0 值 分别清除或设置针对本套接口的信号驱动异步I/O 标志, 它决定是否收取针对本套接口的异步I/O 信号(SIGIO )。 本请求和O_ASYNC 文件状态标志等效, 而该标志可以通过fcntl 的F_SETFL 命令清除或设置。*/ 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; } //设置channel[0]的宿主,控制channel[0]的SIGIO信号只发给这个进程 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; } //若进程执行了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; } //同上 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 ngx_channel = ngx_processes[s].channel[1]; } else { ngx_processes[s].channel[0] = -1; ngx_processes[s].channel[1] = -1; } //设置当前子进程的进程表索引值 ngx_process_slot = s; //创建子进程 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: //设置当前子进程的进程id ngx_pid = ngx_getpid(); //子进程运行执行函数 proc(cycle, data); break; default: break; } ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start %s %P", name, pid); //设置一些进程表项字段 ngx_processes[s].pid = pid; ngx_processes[s].exited = 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; //设置进程表项的一些状态字 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; }
下一节将子进程执行函数