nginx的进程模式
1 进程间的通信方式
使用 UNIX域套接字 socketpair() 异步通讯机制: nginx在创建worker前将先调用 socketpair(int channel[2]) 然后将 channel[0-1]设置为异步通知方式,并注册evnet事件,父进程使用channel[0],子进程使用channel[1]实现双方的通讯.
2 都有哪些通信?
2.1 创建worker进程时
2.2 执行 nginx -s reload 时
2.3 执行 nginx -s stop 时
2.4 执行 nginx -s quit 时
2.5 执行 nginx -s reopen 时
2.6 某worker异常退出时
2.7 概要
- SIGHUP NGX_CMD_QUIT|SIGQUIT
- ./nginx -s reload ---------> master (ngx_reload=1) -------------------------> worker|cache_manager|cache_loader (ngx_quit=1, 处理完待读socket再退出)
- SIGTERM|SIGINT NGX_CMD_TERMINATE|SIGTERM
- ./nginx -s stop -----------------> master (ngx_terminate=1,延迟退出时间) ----------------------------> worker|cache_manager|cache_loader (ngx_terminate=1,直接退出)
- SIGQUIT NGX_CMD_QUIT|SIGQUIT
- ./nginx -s quit ----------> master (ngx_quit=1) ------------------------> worker|cache_manager|cache_loader (ngx_quit=1, 处理完待读socket再退出)
- SIGUSER1 NGX_CMD_REOPEN|SIGUSER1
- ./nginx -s reopen -----------> master (ngx_reopen=1) -------------------------> worker|cache_manager|cache_loader (ngx_reopen=1)
3 通信的内容是什么?
- src/os/unix/ngx_process_cycle.h:
- #define NGX_CMD_OPEN_CHANNEL 1
- #define NGX_CMD_CLOSE_CHANNEL 2
- #define NGX_CMD_QUIT 3
- #define NGX_CMD_TERMINATE 4
- #define NGX_CMD_REOPEN 5
4 创建worker进程时 是如何通信的?
master(父进程):
1> 在进程池 ngx_processes 中为 新worker进程 分配一个位置,如果进程池已满,也就是创建的进程数超过了最大值1024 nginx报错但不退出;
2> 调用 socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) 创建UNIX域套接字对.并设置为异步模式且execve时关闭;
3> 将 新worker进程 的{NGX_CMD_OPEN_CHANNEL, channel[0], 在进程池中的位置, PID} 通过 UNIX域套接字 告知已存在的子进程.
- {
- ngx_start_worker_processes()
- {
- ch.command=NGX_CMD_OPEN_CHANNEL;
- for (i=0; i < n; i++) {
- //...
- 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);
- }
- }
- }
新worker进程:
1> 关闭进程池中其他子进程的 channel[1];
2> 关闭自身的 channel[0];
3> 将自身的 channel[1] 注册event读事件: ngx_channel_handler().
- {
- ngx_worker_process_cycle()
- {
- //...
- ngx_worker_process_init()
- {
- //...
- for (n=0; n < ngx_last_process; n++) {
- if (ngx_processes[n].pid == -1) {
- continue;
- }
- if (n == ngx_process_slot) {
- continue;
- }
- if (ngx_processes[n].channel[1] == -1) {
- continue;
- }
- if (close(ngx_processes[n].channel[1]) == -1) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "close() channel failed");
- }
- }
- if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) {
- }
- if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
- ngx_channel_handler)
- == NGX_ERROR)
- {
- /* fatal */
- exit(2);
- }
- }
- }
- }
自身可以通过 进程池中其他子进程的 channel[0] 收发 其他子进程 的信息.
自身可以通过 自身的 channel[1] 收发 master进程 的信息.
其他子进程:
此时 其他子进程的 UNIX域套接字 收到 master进程的 {NGX_CMD_OPEN_CHANNEL, channel[0], 在进程池中的位置, PID}; 读事件处理函数 ngx_channel_handler()被调用;
1> 获得 新子进程 在进程池中的位置;
2> 保存 新子进程 的 channel[0];
2> 保存 新子进程 的 PID.
- {
- ngx_channel_handler()
- {
- for ( ;; ) {
- n=ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
- switch (ch.command) {
- case NGX_CMD_QUIT:
- ngx_quit=1;
- break;
- case NGX_CMD_TERMINATE:
- ngx_terminate=1;
- break;
- case NGX_CMD_REOPEN:
- ngx_reopen=1;
- break;
- case NGX_CMD_OPEN_CHANNEL:
- ngx_processes[ch.slot].pid=ch.pid;
- ngx_processes[ch.slot].channel[0]=ch.fd;
- break;
- case NGX_CMD_CLOSE_CHANNEL:
- if (close(ngx_processes[ch.slot].channel[0]) == -1) {
- }
- ngx_processes[ch.slot].channel[0]=-1;
- break;
- }
- }
- }
- }
5 执行 nginx -s reload 时
执行 nginx -s reload 的进程:
从main函数开始一直执行到
- if (ngx_signal) {
- return ngx_signal_process(cycle, ngx_signal);
- }
从nginx.pid文件中解析出当前的 master 进程PID, 向其发送 reload 信号
master:
1> 接收到 reload 信号, 信号处理函数 ngx_signal_handler 中将 ngx_reconfigure=1;
2> 进入 ngx_reconfigure 状态; 调用 ngx_init_cycle() 初始化环境,重新加载配置文件, 重新创建 worker进程, cache管理进程, cache加载进程(热代码忽略);
3> 使用 UNIX域套接字 通知老的worker进程, cache管理进程(cache加载进程此时可能存在,也可能已经退出,因为在ngx_cache_loader_process_handler() {exit(0)}), NGX_CMD_QUIT;
这里不通知的进程有: 刚刚创建的worker进程, cache管理进程, cache加载进程, 正在退出中且本次发送信号为 shutdown的进程, 热代码加载中的进程.
4> 如果使用 UNIX域套接字发送 NGX_CMD_QUIT 失败 ,则使用 kill 发送 SIGQUIT, 将这些进程的属性 exiting=1; 如果发送信号时某进程已经退出,则将其属性 exited=1;
5> 过N久之后, 这些被发送信号的进程会退出, master进程会收到内核发送来的 SIGCHLD,将 ngx_reap=1; 并调用 ngx_process_get_status()回收退出的子进程;
6> 回收子进程 ngx_process_get_status();
7> reload状态,也会进入 ngx_reap 状态, 这个状态是否会重新创建 那些老的可再生的子进程 呢?
首先, 重新创建 老的可再生的子进程的条件是:
(ngx_processes[i].exited && ngx_processes[i].respawn && !ngx_processes[i].exiting && !ngx_terminate && !ngx_quit)
reload状态,可以通过观察进程属性 respawn, exiting, exited 的值来判断是否会重新创建 那些老的可再生的子进程. 除了 cache loader 进程的respawn=0, 其他进程 respawn =1. 故只有 exited=1 && exiting=0 时才会重建.
初始值 exiting=0, exited=0;
reload状态, 通知老的子进程退出有三种情况:
如果此时子进程已经退出, master 还未来得及回收,则将 exited=1, exiting=0, ngx_reap=1. [可能重建]
我们来看这种 [可能重建] 的情况.
master的for(;;)逻辑:
- for()
- {
- if (delay) {}
- sigsuspend();
- ngx_time_update();
- if (ngx_reap) {}
- if (!live && (ngx_terminate || ngx_quit)) {}
- if (ngx_terminate) {}
- if (ngx_quit) {}
- if (ngx_reconfigure) {}
- if (ngx_restart) {}
- if (ngx_reopen) {}
- if (ngx_change_binary) {}
- if (ngx_noaccept) {}
- }
如果情况 出现, 说明子进程已经退出, master还未回收, 等本次 if (ngx_reconfigure) {} 会回到for(;;), sigsuspend();时会收到 SIGCHLD,进入 ngx_signal_handler() ngx_reap=1; 进入 ngx_reap 状态, 此时 exited=1, exiting=0, 会导致:
(1) 这个进程会被创建
(2) 这个老的进程占用的master的进程数组的位置不会释放. 也就是 pid 不会等于1, 而且该进程的属性始终 exited=1, exiting=0,
(3) 由于上面的 exited=1, exiting=0, 所以下次进入 ngx_reap状态 时,还会被创建, 下下次进入 ngx_reap状态 时还会被创建,如果不断的进入 ngx_reap 状态就会导致master的进程数组用完.
最终的现象是: 你本来定义4个worker进程, 如果这种情况发生, 每reload一次,就会增加一个worker进程. 直到nginx 退出. 我觉得这是nginx的一个bug.
- {
- /* 接受到 reload 信号: 1 "SIGHUP" ngx_signal_handler "reload" */
- ngx_signal_handler()
- {
- switch (ngx_process) {
- case NGX_PROCESS_MASTER:
- case NGX_PROCESS_SINGLE:
- switch (signo) {
- //...
- case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
- ngx_reconfigure=1;
- action=", reconfiguring";
- break;
- case SIGCHLD:
- ngx_reap=1;
- break;
- //...
- }
- //...
- }
- }
- ngx_master_process_cycle()
- {
- //...
- for (;;)
- {
- if (ngx_reap)
- {
- ngx_reap=0;
- ngx_reap_children(cycle);
- }
- if (ngx_reconfigure) {
- ngx_reconfigure=0;
- /* 热代码加载 */
- if (ngx_new_binary) {
- ngx_start_worker_processes(cycle, ccf->worker_processes,
- NGX_PROCESS_RESPAWN);
- ngx_start_cache_manager_processes(cycle, 0);
- ngx_noaccepting=0;
- continue;
- }
- /* 重新初始化,加载配置 */
- cycle=ngx_init_cycle(cycle);
- ngx_cycle=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_JUST_RESPAWN);
- /* 重新创建新的 缓存池管理进程 和 缓存池加载进程 */
- ngx_start_cache_manager_processes(cycle, 1);
- /* 通知 */
- ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
- {
- switch (signo) {
- case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
- ch.command=NGX_CMD_QUIT;
- break;
- default:
- ch.command=0;
- }
- for (i=0; i < ngx_last_process; i++) {
- /* 如果是 新创建的进程(NGX_PROCESS_JUST_RESPAWN), 正在退出的进程(ngx_processes[i]->exiting), 热代码加载的进程, 则不通知他们;
- 否则通过 UNIX域套接字 告知.
- */
- if (ch.command) {
- if (ngx_write_channel(ngx_processes[i].channel[0],
- &ch, sizeof(ngx_channel_t), cycle->log)
- == NGX_OK)
- {
- if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {
- ngx_processes[i].exiting=1; /* 将该进程的状态 exiting=1 */
- }
- continue;
- }
- }
- if (kill(ngx_processes[i].pid, signo) == -1) {
- if (err == NGX_ESRCH) { /* 该子进程进程为僵死进程 */
- ngx_processes[i].exited=1;
- ngx_processes[i].exiting=0;
- ngx_reap=1;
- }
- continue;
- }
- /* 除了 reopen 信号外, 其他信号都会使子进程退出, 这里将那些子进程的状态改为正在退出的状态.*/
- if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {
- ngx_processes[i].exiting=1;
- }
- }
- }
- }
- }
- }
- ngx_process_get_status()
- {
- /* 循环回收 */
- for ( ;; ) {
- pid=waitpid(-1, &status, WNOHANG);
- /* 如果子进程持有 accept 锁, 则释放. */
- if (ngx_accept_mutex_ptr) {
- ngx_atomic_cmp_set(ngx_accept_mutex_ptr, pid, 0);
- }
- /* 将该进程在master进程池中的属性 status=其退出码, exited=1, */
- for (i=0; i < ngx_last_process; i++) {
- if (ngx_processes[i].pid == pid) {
- ngx_processes[i].status=status;
- ngx_processes[i].exited=1;
- process=ngx_processes[i].name;
- break;
- }
- }
- /* 打印日志: 如果子进程被信号中断,则打印出信号编号, 否则打印出其退出码*/
- //...
- /* nginx子进程有个 可再生(respawn, NGX_PROCESS_RESPAWN)属性: 如果该属性有效 master检测其退出后会再次创建它.
- 其中 worker进程, cache管理进程创建后,对应的 ngx_processes[i]->respawn=1; cache加载进程 ngx_processes[i]->respawn=0;意思推出后不需要再次创建它.
- 子进程的退出方式有:
- 被信号中断
- 自身退出,退出码 0 对应 cache 加载进程
- 自身退出,退出码 2 对应reload时,被通知的哪些旧的 worker进程, cache管理进程, cache加载进程(如果它reload时它还存在)退出时的退出码
- 这里就是使用这中机制来确定 master回收这些进程时,是否重新创建它.
- 退出码为2且拥有可再生权限,说明在重新加载配置文件, 这些退出的进程无须再创建他们, 将其 respawn=0;
- */
- if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) {
- ngx_processes[i].respawn=0;
- }
- }
- }
- ngx_reap_children()
- {
- ch.command=NGX_CMD_CLOSE_CHANNEL;
- for (i=0; i < ngx_last_process; i++) {
- if (ngx_processes[i].exited) {
- if (!ngx_processes[i].detached) {
- /* 清理子进程的 UNIX域套接字 资源, 通过 UNIX域套接字告知其他存活子进程 NGX_CMD_CLOSE_CHANNEL */
- }
- //...
- } else if (ngx_processes[i].exiting || !ngx_processes[i].detached) {
- live=1;
- }
- }
- return live;
- }
- }
worker:
1> 接收 master发送来的 SIGQUIT 信号, 信号处理函数 ngx_signal_handler 中将 ngx_quit=1; 或者 UNIX域套接字收到 NGX_CMD_QUIT 将 ngx_quit=1;
2> 主逻辑进入 ngx_quit 状态, 将其修改进程标题为"worker process is shutting down"; 关闭监听socket,转入 ngx_exiting 状态
3> ngx_exiting 状态中 调用连接池中所有连接的处理函数,并将其 close=1;
4> 退出. 在 ngx_exiting 状态中 监听socket 已经关闭,意味着不会再有新的请求注册到 事件处理器 中, 如果 事件处理器 中没有任何事件了,此时worker进程就可以退出了.
- {
- ngx_channel_handler()
- {
- for ( ;; ) {
- n=ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
- switch (ch.command) {
- case NGX_CMD_QUIT:
- ngx_quit=1;
- break;
- }
- }
- }
- ngx_signal_handler()
- {
- switch (ngx_process) {
- //...
- case NGX_PROCESS_WORKER:
- case NGX_PROCESS_HELPER:
- switch (signo) {
- //...
- case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
- ngx_quit=1;
- action=", shutting down";
- break;
- //...
- }
- //...
- break;
- }
- }
- ngx_worker_process_cycle()
- {
- if (ngx_exiting) {
- c=cycle->connections;
- for (cycle->connection) {
- if (c[i].fd != -1 && c[i].idle) {
- c[i].close=1;
- c[i].read->handler(c[i].read);
- }
- }
- if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)
- {
- ngx_worker_process_exit(cycle);
- }
- }
- if (ngx_quit) {
- ngx_quit=0;
- ngx_setproctitle("worker process is shutting down");
- if (!ngx_exiting) {
- ngx_close_listening_sockets(cycle);
- ngx_exiting=1; /* 将 ngx_exiting 置为有效. */
- }
- }
- }
- }
cache_manager:
1> 接收 master发送来的 SIGQUIT 信号, 信号处理函数 ngx_signal_handler 将 ngx_quit=1;
2> 进入 ngx_quit 状态, 进程直接 exit(0);
- {
- ngx_cache_manager_process_cycle()
- {
- //...
- for (;;)
- {
- if (ngx_terminate || ngx_quit) {
- exit(0);
- }
- }
- }
- }
cache_loader:
和 cache_manager进程一样, 因为cache加载进程加载完毕后就自动退出了, 只有在加载完毕前执行 nginx -s stop 才会执行这里的逻辑.
6 执行 nginx -s stop 时
执行 nginx -s stop 的进程:
从main函数开始一直执行到
- if (ngx_signal) {
- return ngx_signal_process(cycle, ngx_signal);
- }
从nginx.pid文件中解析出当前的 master 进程PID, 向其发送 stop 信号
master:
1> 接收 stop (SIGINT 或 SIGTERM)信号, 信号处理函数 ngx_signal_handler 将 ngx_terminate=1;
2> 进入 ngx_terminate 状态; 向子进程通过 UNIX域套接字 发送 NGX_CMD_TERMINATE, 如果发送失败,则通过 信号机制 发送 SIGTERM 信号.
3> master为通过 setitimer() 每个子进程预留50毫秒(最大1000毫秒)的延迟退出时间, 如果一个子进程50毫秒未退出,则它的延迟退出时间增加一倍,直到进程的累计延迟退出时间超过1000毫秒,则向还未退出的子进程发送 SIGKILL 信号
4> 回收退出的子进程. 收到 SIGCHLD, 将 ngx_reap=1; 进入 ngx_process_get_status(), exited=1, 此时 exiting=0;
5> 进入 ngx_reap 状态, 所有回收的子进程都不会被重建, 因为 ngx_terminate=1; 这里将 pid=-1; 返回live=0
6> 此时没有存活的子进程(即 ngx_reap 状态返回live=0) 或 ngx_terminate=1 或者 ngx_quit=1, 则master 进入 退出 状态 ngx_master_process_exit(); 调用所有模块的 exit_master() 钩子函数, 关闭 监听socket, 然后 exit(0);
- {
- /* 接收信号 15 "SIGTERM" ngx_signal_handler "stop" */
- ngx_signal_handler()
- {
- switch (ngx_process) {
- case NGX_PROCESS_MASTER:
- case NGX_PROCESS_SINGLE:
- switch (signo) {
- //...
- case ngx_signal_value(NGX_TERMINATE_SIGNAL):
- case SIGINT:
- ngx_terminate=1;
- break;
- case SIGALRM:
- ngx_sigalrm=1;
- break;
- //...
- }
- //...
- }
- }
- ngx_master_process_cycle()
- {
- for (;;)
- {
- if (delay) {
- if (ngx_sigalrm) {
- sigio=0;
- delay *= 2;
- ngx_sigalrm=0;
- }
- itv.it_interval.tv_sec=0;
- itv.it_interval.tv_usec=0;
- itv.it_value.tv_sec=delay / 1000;
- itv.it_value.tv_usec=(delay % 1000 ) * 1000;
- if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
- }
- }
- sigsuspend(&set);
- ngx_time_update();
- if (ngx_reap) {
- ngx_reap = 0;
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
- live = ngx_reap_children(cycle);
- }
- if (!live && (ngx_terminate || ngx_quit)) {
- ngx_master_process_exit(cycle);
- }
- /* 退出机制: nginx -s stop
- master 收到 SIGTERM 或者 SIGINT 时, 进入 ngx_terminate 状态, 然后给所有子进程通过 UNIX域套接字发送 发送 NGX_CMD_QUIT, 通过kill() SIGTERM 信号
- 给每个子进程预留 50 毫秒的退出时间, 使用setitmer()延迟 n*50 毫秒, 当 n*50 还有子进程未退出, 则延迟时间增加为 n*2*50 以此类推. 如果子进程在 1000 毫秒内还没有全部退出,则对还在运行的子进程发送 SIGKILL.
- SIGKILL 是不能屏蔽也不能注册信号处理函数的信号, 动作为是 终止.
- */
- if (ngx_terminate) {
- if (delay == 0) {
- delay=50;
- }
- if (sigio) {
- sigio--;
- continue;
- }
- sigio=ccf->worker_processes + 2 /* cache processes */;
- if (delay > 1000) {
- ngx_signal_worker_processes(cycle, SIGKILL);
- } else {
- ngx_signal_worker_processes(cycle,
- ngx_signal_value(NGX_TERMINATE_SIGNAL));
- }
- continue;
- }
- }
- }
- }
worker:
1> 接收 master发送来的 SIGTERM 信号, 信号处理函数 ngx_signal_handler 中将 ngx_terminate=1; 或者 UNIX域套接字收到 NGX_CMD_TERMINATE, 将 ngx_terminate=1;
2> 主逻辑进入 ngx_terminate 状态, 执行 ngx_worker_process_exit(), 调用 exit_process 钩子函数, 然后exit(0);
3> 如果1000毫秒后还未退出,会收到master发来的 SIGKILL 信号,当时就退出了.
- {
- ngx_channel_handler()
- {
- for ( ;; ) {
- n=ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
- switch (ch.command) {
- case NGX_CMD_TERMINATE:
- ngx_terminate=1;
- break;
- }
- }
- }
- ngx_signal_handler()
- {
- switch (ngx_process) {
- //...
- case NGX_PROCESS_WORKER:
- case NGX_PROCESS_HELPER:
- switch (signo) {
- //...
- case ngx_signal_value(NGX_TERMINATE_SIGNAL):
- ngx_terminate=1;
- action=", shutting down";
- break;
- //...
- }
- //...
- }
- }
- ngx_worker_process_cycle()
- {
- //...
- ngx_process_events_and_timers(cycle);
- if (ngx_terminate) {
- ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
- ngx_worker_process_exit(cycle);
- }
- }
- }
7 执行 nginx -s quit 时
执行 nginx -s quit 的进程:
从main函数开始一直执行到
- if (ngx_signal) {
- return ngx_signal_process(cycle, ngx_signal);
- }
从nginx.pid文件中解析出当前的 master 进程PID, 向其发送 quit 信号
master:
1> 接收 quit (SIGQUIT)信号, 信号处理函数 ngx_signal_handler 将 ngx_quit=1;
2> 进入 ngx_quit 状态, 向子进程通过 UNIX域套接字 发送 NGX_CMD_QUIT, 如果发送失败,则通过 信号机制 发送 SIGQUIT 信号.
3> 关闭 监听socket.
4> 回收退出的子进程. 收到 SIGCHLD, 将 ngx_reap=1; 进入 ngx_process_get_status(), exited=1, 此时 exiting=0;
5> 进入 ngx_reap 状态, 所有回收的子进程都不会被重建, 因为 ngx_terminate=1; 这里将 pid=-1;
6> 此时没有存活的子进程(即 ngx_reap 状态返回live=0) 或 ngx_terminate=1 或者 ngx_quit=1, 则master 进入退出状态 ngx_master_process_exit(); 调用所有模块的 exit_master() 钩子函数, 关闭 监听socket, 然后 exit(0);
- {
- // 接收信号 3 "SIGQUIT" ngx_signal_handler "quit"
- ngx_signal_handler()
- {
- switch (ngx_process) {
- case NGX_PROCESS_MASTER:
- case NGX_PROCESS_SINGLE:
- switch (signo) {
- //...
- case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
- ngx_quit=1;
- break;
- //...
- }
- //...
- }
- }
- ngx_master_process_cycle()
- {
- for (;;)
- {
- //...
- sigsuspend(&set);
- ngx_time_update();
- if (ngx_reap) {
- ngx_reap = 0;
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
- live = ngx_reap_children(cycle);
- }
- if (!live && (ngx_terminate || ngx_quit)) {
- ngx_master_process_exit(cycle);
- }
- if (ngx_quit) {
- ngx_signal_worker_processes(cycle,
- ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
- ls = cycle->listening.elts;
- for (n = 0; n < cycle->listening.nelts; n++) {
- if (ngx_close_socket(ls[n].fd) == -1) {
- ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
- ngx_close_socket_n " %V failed",
- &ls[n].addr_text);
- }
- }
- cycle->listening.nelts = 0;
- continue;
- }
- }
- }
- }
worker:
1> 接收 master发送来的 SIGQUIT 信号, 信号处理函数 ngx_signal_handler 中将 ngx_quit=1; 或者 UNIX域套接字收到 NGX_CMD_QUIT 将 ngx_quit=1;
2> 主逻辑进入 ngx_quit 状态, 将其修改进程标题为"worker process is shutting down"; 关闭监听socket,转入 ngx_exiting 状态
3> ngx_exiting 状态中 调用连接池中所有连接的处理函数,并将其 close=1;
4> 退出. 在 ngx_exiting 状态中 监听socket 已经关闭,意味着不会再有新的请求注册到 事件处理器 中, 如果 事件处理器 中没有任何事件了,此时worker进程就可以退出了.
- {
- ngx_channel_handler()
- {
- for ( ;; ) {
- n=ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
- switch (ch.command) {
- case NGX_CMD_QUIT:
- ngx_quit=1;
- break;
- }
- }
- }
- ngx_signal_handler()
- {
- switch (ngx_process) {
- //...
- case NGX_PROCESS_WORKER:
- case NGX_PROCESS_HELPER:
- switch (signo) {
- //...
- case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
- ngx_quit=1;
- action=", shutting down";
- break;
- //...
- }
- //...
- break;
- }
- }
- ngx_worker_process_cycle()
- {
- if (ngx_exiting) {
- c=cycle->connections;
- for (cycle->connection) {
- if (c[i].fd != -1 && c[i].idle) {
- c[i].close=1;
- c[i].read->handler(c[i].read);
- }
- }
- if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)
- {
- ngx_worker_process_exit(cycle);
- }
- }
- if (ngx_quit) {
- ngx_quit=0;
- ngx_setproctitle("worker process is shutting down");
- if (!ngx_exiting) {
- ngx_close_listening_sockets(cycle);
- ngx_exiting=1; /* 将 ngx_exiting 置为有效. */
- }
- }
- }
- }
cache_manager:
1> 接收 master发送来的 SIGQUIT 信号, 信号处理函数 ngx_signal_handler 将 ngx_quit=1;
2> 进入 ngx_quit 状态 ngx_worker_process_exit(), 调用 exit_process 钩子函数, 然后exit(0);
- {
- ngx_cache_manager_process_cycle()
- {
- //...
- for (;;)
- {
- if (ngx_terminate || ngx_quit) {
- exit(0);
- }
- }
- }
- }
cache_loader:
和 cache_manager进程一样, 因为cache加载进程加载完毕后就自动退出了, 只有在加载完毕前执行 nginx -s stop 才会执行这里的逻辑.
8 执行 nginx -s reopen 时
执行 nginx -s reopen 的进程:
从main函数开始一直执行到
- if (ngx_signal) {
- return ngx_signal_process(cycle, ngx_signal);
- }
从nginx.pid文件中解析出当前的 master 进程PID, 向其发送 reopen 信号
master:
1> 接收 reopen (SIGUSER1)信号, 信号处理函数 ngx_signal_handler 将 ngx_reopen=1;
2> 进入 ngx_reopen 状态, 向子进程通过 UNIX域套接字 发送 NGX_CMD_REOPEN, 如果发送失败,则通过 信号机制 发送 SIGUSER1 信号.
3> 进入 ngx_reopen_files() 重新打开 cycle->open_files 中的文件, 有: "logs/error.log", "logs/access.log".
- {
- // 接收信号 10 "SIGUSR1" ngx_signal_handler "reopen"
- ngx_signal_handler()
- {
- switch (ngx_process) {
- case NGX_PROCESS_MASTER:
- case NGX_PROCESS_SINGLE:
- switch (signo) {
- //...
- case ngx_signal_value(NGX_REOPEN_SIGNAL):
- ngx_reopen=1;
- break;
- //...
- }
- //...
- }
- }
- ngx_master_process_cycle()
- {
- for (;;)
- {
- //...
- sigsuspend(&set);
- ngx_time_update();
- if (ngx_reopen) {
- ngx_reopen = 0;
- ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
- ngx_reopen_files(cycle, ccf->user);
- ngx_signal_worker_processes(cycle,
- ngx_signal_value(NGX_REOPEN_SIGNAL));
- }
- }
- }
- }
worker:
1> 接收 master 发送来的 SIGUSER1 信号, 信号处理函数 ngx_signal_handler 中将 ngx_reopen=1; 或者 UNIX域套接字收到 NGX_CMD_REOPEN 将 ngx_reopen=1;
2> 进入 ngx_reopen 状态, 进入 ngx_reopen_files() 重新打开 cycle->open_files 中的文件, 有: "logs/error.log", "logs/access.log".
- {
- ngx_channel_handler()
- {
- for ( ;; ) {
- n=ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
- switch (ch.command) {
- case NGX_CMD_REOPEN:
- ngx_reopen=1;
- break;
- }
- }
- }
- ngx_signal_handler()
- {
- switch (ngx_process) {
- //...
- case NGX_PROCESS_WORKER:
- case NGX_PROCESS_HELPER:
- switch (signo) {
- //...
- case ngx_signal_value(NGX_REOPEN_SIGNAL):
- ngx_reopen=1;
- action=", shutting down";
- break;
- //...
- }
- //...
- break;
- }
- }
- ngx_worker_process_cycle()
- {
- if (ngx_reopen) {
- ngx_reopen = 0;
- ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
- ngx_reopen_files(cycle, -1);
- }
- }
- }
9 某worker异常退出时
master:
1> master收到内核发送来的 SIGCHLD 信号, 信号处理函数 ngx_signal_handler() 将 ngx_reap=1, 调用 ngx_process_get_status() 回收子进程,
2> ngx_process_get_status() 回收子进程, 将 exited=1, 此时 exiting=0;
3> 进入 ngx_reap 状态, ngx_reap_children(), 通知其他子进程该子进程已经退出,请关闭它的channel[0], 重新创建异常退出的worker进程. 重新创建 老的可再生的子进程的条件是:
(ngx_processes[i].exited && ngx_processes[i].respawn && !ngx_processes[i].exiting && !ngx_terminate && !ngx_quit)
这里除了 cache_loader 子进程(respawn=0)外, worker, cache_manager子进程(respawn=1)异常退出时都会被创建.
- {
- ngx_signal_handler()
- {
- switch (ngx_process) {
- case NGX_PROCESS_MASTER:
- case NGX_PROCESS_SINGLE:
- switch (signo) {
- case SIGCHLD:
- ngx_reap=1;
- break;
- }
- }
- if (signo == SIGCHLD) {
- ngx_process_get_status();
- }
- }
- ngx_master_process_cycle()
- {
- for (;;)
- {
- if (ngx_reap)
- {
- ngx_reap=0;
- live=ngx_reap_children(cycle);
- }
- }
- }
- ngx_reap_children()
- {
- ch.command=NGX_CMD_CLOSE_CHANNEL;
- for (i=0; i < ngx_last_process; i++) {
- if (ngx_processes[i].exited) {
- if (!ngx_processes[i].detached) {
- ngx_close_channel(ngx_processes[i].channel, cycle->log);
- ngx_processes[i].channel[0]=-1;
- ngx_processes[i].channel[1]=-1;
- ch.pid=ngx_processes[i].pid;
- ch.slot=i;
- for (n=0; n < ngx_last_process; n++) {
- if (ngx_processes[n].exited
- || ngx_processes[n].pid == -1
- || ngx_processes[n].channel[0] == -1)
- {
- continue;
- }
- ngx_write_channel(ngx_processes[n].channel[0],
- &ch, sizeof(ngx_channel_t), cycle->log);
- }
- }
- if (ngx_processes[i].respawn
- && !ngx_processes[i].exiting
- && !ngx_terminate
- && !ngx_quit)
- {
- if (ngx_spawn_process(cycle, ngx_processes[i].proc,
- ngx_processes[i].data,
- ngx_processes[i].name, i)
- == NGX_INVALID_PID)
- {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
- "could not respawn %s",
- ngx_processes[i].name);
- continue;
- }
- ch.command=NGX_CMD_OPEN_CHANNEL;
- 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);
- live=1;
- continue;
- }
- if (ngx_processes[i].pid == ngx_new_binary) {
- ccf=(ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
- ngx_core_module);
- if (ngx_rename_file((char *) ccf->oldpid.data,
- (char *) ccf->pid.data)
- != NGX_OK)
- {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- ngx_rename_file_n " %s back to %s failed "
- "after the new binary process \"%s\" exited",
- ccf->oldpid.data, ccf->pid.data, ngx_argv[0]);
- }
- ngx_new_binary=0;
- if (ngx_noaccepting) {
- ngx_restart=1;
- ngx_noaccepting=0;
- }
- }
- if (i == ngx_last_process - 1) {
- ngx_last_process--;
- } else {
- ngx_processes[i].pid=-1;
- }
- } else if (ngx_processes[i].exiting || !ngx_processes[i].detached) {
- live=1;
- }
- }
- return live;
- }
- }
worker:
1> 接收ngx_reap_children()使用 UNIX域套接字 发送来的 NGX_CMD_CLOSE_CHANNEL;执行关闭对应进程的 UNIX域套接字,清理pid及所在进程池的位置.
2> 接收ngx_reap_children()创建新进程时, 使用UNIX域套接字发送来的 NGX_CMD_OPEN_CHANNEL;执行关闭对应进程的 UNIX域套接字,清理pid及所在进程池的位置.
cache_manager:
1> 接收ngx_reap_children()使用 UNIX域套接字 发送来的 NGX_CMD_CLOSE_CHANNEL;执行关闭对应进程的 UNIX域套接字,清理pid及所在进程池的位置.
2> 接收ngx_reap_children()创建新进程时, 使用UNIX域套接字发送来的 NGX_CMD_OPEN_CHANNEL;执行关闭对应进程的 UNIX域套接字,清理pid及所在进程池的位置.
cache_loader:
如果此时 cache_loader 还存在, 它也会收到如下:
1> 接收ngx_reap_children()使用 UNIX域套接字 发送来的 NGX_CMD_CLOSE_CHANNEL;执行关闭对应进程的 UNIX域套接字,清理pid及所在进程池的位置.
2> 接收ngx_reap_children()创建新进程时, 使用UNIX域套接字发送来的 NGX_CMD_OPEN_CHANNEL;执行关闭对应进程的 UNIX域套接字,清理pid及所在进程池的位置.