nginx的进程模式
1 进程间的通信方式
使用 UNIX域套接字 socketpair() 异步通讯机制: nginx在创建worker前将先调用 socketpair(int channel[2]) 然后将 channel[0-1]设置为异步通知方式,并注册evnet事件,父进程使用channel[0],子进程使用channel[1]实现双方的通讯.
1.1 创建子进程时用到的标示
/* NGX_PROCESS_XXX 用来标示创建子进程的属性.
NGX_PROCESS_NORESPAWN:
子进程退出时,父进程不会再次创建, 该标记用在创建 "cache loader process".
NGX_PROCESS_JUST_SPAWN:
当 nginx -s reload 时, 如果还有未加载的 proxy_cache_path, 则需要再次创建 "cache loader process"加载,并用 NGX_PROCESS_JUST_SPAWN给这个进程做记号,防止 "master会向老的worker进程,老的cache manager进程,老的cache loader进程(如果存在)发送NGX_CMD_QUIT或SIGQUIT" 时,误以为这个进程是老的cache loader进程.
NGX_PROCESS_RESPAWN:
子进程异常退出时,master会重新创建它, 如当worker或cache manager异常退出时,父进程会重新创建它.
NGX_PROCESS_JUST_RESPAWN:
当 nginx -s reload 时, master会向老的worker进程,老的cache manager进程,老的cache loader进程(如果存在)发送 ngx_write_channel(NGX_CMD_QUIT)(如果失败则发送SIGQUIT信号); 该标记用来标记进程数组中哪些是新创建的子进程;其他的就是老的子进程.
NGX_PROCESS_DETACHED: 热代码替换
*/
#define NGX_PROCESS_NORESPAWN -1
#define NGX_PROCESS_JUST_SPAWN -2
#define NGX_PROCESS_RESPAWN -3
#define NGX_PROCESS_JUST_RESPAWN -4
#define NGX_PROCESS_DETACHED -5
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)
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状态, 通知老的子进程退出有三种情况:
<A> 使用 UNIX域套接字 发送 NGX_CMD_QUIT, 发送成功将 exiting=1, 此时 exited 还是 0. [不会重建]
<B> 情况<A> 失败, 使用 kill 发送 SIGQUIT, 发送成功, 此时子进程还未退出, 将 exiting=1, 此时 exited 还是 0. [不会重建]
<C> 如果此时子进程已经退出, 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) {}
}
如果情况 <C> 出现, 说明子进程已经退出, 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;
//...
/* 重新初始化,加载配置 */
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;
}
}
/* 打印日志: 如果子进程被信号中断,则打印出信号编号, 否则打印出其退出码*/
//...
}
}
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;
//...
}
//...
}
}
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;
//...
}
//...
}
}
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及所在进程池的位置.
----end-----
From: GS