worker 的信号处理 -- nginx worker 进程主循环流程

2015-05-10 17:57:55   最后更新: 2015-05-10 17:57:55   访问数量:1594




在 nginx 的初始化进程中,有一个重要的步骤,就是执行 ngx_init_signals 函数,在这个函数中,为通过 sigaction 系统调用指定了程序的信号处理函数 -- ngx_signal_handler

初始化进程 fork 出了 master 进程,master 进程又 fork 出了 worker 进程,因此,他们都是用默认的信号处理函数 -- ngx_signal_handler

 

// void ngx_signal_handler(int signo) // 信号的默认处理函数 {{{ void ngx_signal_handler(int signo) { char *action; ngx_int_t ignore; ngx_err_t err; ngx_signal_t *sig; ignore = 0; err = ngx_errno; for (sig = signals; sig->signo != 0; sig++) { if (sig->signo == signo) { break; } } // 更新 cached_err_log_time ngx_time_sigsafe_update(); action = ""; switch (ngx_process) { case NGX_PROCESS_MASTER: case NGX_PROCESS_SINGLE: switch (signo) { case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): ngx_quit = 1; action = ", shutting down"; break; case ngx_signal_value(NGX_TERMINATE_SIGNAL): case SIGINT: ngx_terminate = 1; action = ", exiting"; break; case ngx_signal_value(NGX_NOACCEPT_SIGNAL): if (ngx_daemonized) { ngx_noaccept = 1; action = ", stop accepting connections"; } break; case ngx_signal_value(NGX_RECONFIGURE_SIGNAL): ngx_reconfigure = 1; action = ", reconfiguring"; break; case ngx_signal_value(NGX_REOPEN_SIGNAL): ngx_reopen = 1; action = ", reopening logs"; break; case ngx_signal_value(NGX_CHANGEBIN_SIGNAL): if (getppid() > 1 || ngx_new_binary > 0) { /* * Ignore the signal in the new binary if its parent is * not the init process, i.e. the old binary's process * is still running. Or ignore the signal in the old binary's * process if the new binary's process is already running. */ action = ", ignoring"; ignore = 1; break; } ngx_change_binary = 1; action = ", changing binary"; break; case SIGALRM: ngx_sigalrm = 1; break; case SIGIO: ngx_sigio = 1; break; case SIGCHLD: ngx_reap = 1; break; } break; case NGX_PROCESS_WORKER: case NGX_PROCESS_HELPER: switch (signo) { case ngx_signal_value(NGX_NOACCEPT_SIGNAL): if (!ngx_daemonized) { break; } ngx_debug_quit = 1; case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): ngx_quit = 1; action = ", shutting down"; break; case ngx_signal_value(NGX_TERMINATE_SIGNAL): case SIGINT: ngx_terminate = 1; action = ", exiting"; break; case ngx_signal_value(NGX_REOPEN_SIGNAL): ngx_reopen = 1; action = ", reopening logs"; break; case ngx_signal_value(NGX_RECONFIGURE_SIGNAL): case ngx_signal_value(NGX_CHANGEBIN_SIGNAL): case SIGIO: action = ", ignoring"; break; } break; } ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, "signal %d (%s) received%s", signo, sig->signame, action); if (ignore) { ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0, "the changing binary signal is ignored: " "you should shutdown or terminate " "before either old or new binary's process"); } if (signo == SIGCHLD) { ngx_process_get_status(); } ngx_set_errno(err); } // }}}

 

 

在函数中,通过对不同信号的响应,置位了不同的全局变量

 

worker 进程主要响应来自于 master 进程的信号:

nginx worker 进程处理的信号
信号全局变量意义
SIGQUITngx_quit关闭进程
SIGTERM  SIGINTngx_terminate强制退出
USR1ngx_reopen重新打开所有文件
WHICHngx_debug_quit仅用于测试

 

// worker 进程主循环 {{{ for ( ;; ) { // 准备退出 if (ngx_exiting) { c = cycle->connections; // 遍历连接池中所有连接并关闭 for (i = 0; i < cycle->connection_n; i++) { /* THREAD: lock */ 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_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); // 关闭子进程 ngx_worker_process_exit(cycle); } } ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle"); // 事件驱动函数 ngx_process_events_and_timers(cycle); // 收到 SIGTERM 或 SIGINT 信号后在 ngx_signal_handler 函数中置位 if (ngx_terminate) { ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); ngx_worker_process_exit(cycle); } // 收到 SIGQUIT 信号后在 ngx_signal_handler 函数中置位 if (ngx_quit) { ngx_quit = 0; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "gracefully shutting down"); ngx_setproctitle("worker process is shutting down"); if (!ngx_exiting) { ngx_close_listening_sockets(cycle); ngx_exiting = 1; } } // 收到 SIGUSR1 信号后在 ngx_signal_handler 函数中置位 if (ngx_reopen) { ngx_reopen = 0; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs"); ngx_reopen_files(cycle, -1); } } // }}}

 

在 worker 进程的主循环中,每次事件处理结束后都会对上述信号置位的全局变量进行检测,并作出相应的处理

 

 

ngx_quit 置位的处理

如果收到 SIGQUIT 信号正常退出,则会调用 ngx_close_listening_sockets 函数关闭所有监听的套接字并置位 ngx_exiting

// void ngx_close_listening_sockets(ngx_cycle_t *cycle) // 关闭所有监听的套接字 {{{ void ngx_close_listening_sockets(ngx_cycle_t *cycle) { ngx_uint_t i; ngx_listening_t *ls; ngx_connection_t *c; if (ngx_event_flags & NGX_USE_IOCP_EVENT) { return; } ngx_accept_mutex_held = 0; ngx_use_accept_mutex = 0; ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { c = ls[i].connection; if (c) { if (c->read->active) { if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { ngx_del_conn(c, NGX_CLOSE_EVENT); } else if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { /* * it seems that Linux-2.6.x OpenVZ sends events * for closed shared listening sockets unless * the events was explicitly deleted */ ngx_del_event(c->read, NGX_READ_EVENT, 0); } else { ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); } } ngx_free_connection(c); c->fd = (ngx_socket_t) -1; } ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, "close listening %V #%d ", &ls[i].addr_text, ls[i].fd); if (ngx_close_socket(ls[i].fd) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, ngx_close_socket_n " %V failed", &ls[i].addr_text); } #if (NGX_HAVE_UNIX_DOMAIN) if (ls[i].sockaddr->sa_family == AF_UNIX && ngx_process <= NGX_PROCESS_MASTER && ngx_new_binary == 0) { u_char *name = ls[i].addr_text.data + sizeof("unix:") - 1; if (ngx_delete_file(name) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, ngx_delete_file_n " %s failed", name); } } #endif ls[i].fd = (ngx_socket_t) -1; } cycle->listening.nelts = 0; } // }}}

 

 

程序中遍历了所有的监听连接,分别调用它们的 ngx_event_actions.del 函数,并最终执行 close,关闭了所有的监听套接字

 

ngx_exiting 置位的处理

if (ngx_exiting) { c = cycle->connections; // 遍历连接池中所有连接并关闭 for (i = 0; i < cycle->connection_n; i++) { /* THREAD: lock */ 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_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); // 关闭子进程 ngx_worker_process_exit(cycle); } }

 

 

这段代码首先关闭了所有活动连接,并执行他们的读函数,以完成数据的处理

如果定时器中已经没有任务,则可以执行 ngx_worker_process_exit 退出

 

worker 进程的退出 -- ngx_worker_process_exit

// static void ngx_worker_process_exit(ngx_cycle_t *cycle) // 子进程退出 {{{ static void ngx_worker_process_exit(ngx_cycle_t *cycle) { ngx_uint_t i; ngx_connection_t *c; #if (NGX_THREADS) ngx_terminate = 1; ngx_wakeup_worker_threads(cycle); #endif // 执行各个模块的 exit_process 回调函数 for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->exit_process) { ngx_modules[i]->exit_process(cycle); } } // 不是强制退出的情况下,如果还有打开的连接,则异常退出 if (ngx_exiting) { c = cycle->connections; for (i = 0; i < cycle->connection_n; i++) { if (c[i].fd != -1 && c[i].read && !c[i].read->accept && !c[i].read->channel && !c[i].read->resolver) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "*%uA open socket #%d left in connection %ui", c[i].number, c[i].fd, i); ngx_debug_quit = 1; } } if (ngx_debug_quit) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "aborting"); // 断言,raise SIGSTOP 信号给进程 ngx_debug_point(); } } /* * Copy ngx_cycle->log related data to the special static exit cycle, * log, and log file structures enough to allow a signal handler to log. * The handler may be called when standard ngx_cycle->log allocated from * ngx_cycle->pool is already destroyed. */ ngx_exit_log = *ngx_log_get_file_log(ngx_cycle->log); ngx_exit_log_file.fd = ngx_exit_log.file->fd; ngx_exit_log.file = &ngx_exit_log_file; ngx_exit_log.next = NULL; ngx_exit_log.writer = NULL; ngx_exit_cycle.log = &ngx_exit_log; ngx_exit_cycle.files = ngx_cycle->files; ngx_exit_cycle.files_n = ngx_cycle->files_n; ngx_cycle = &ngx_exit_cycle; // 销毁内存池 ngx_destroy_pool(cycle->pool); ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, "exit"); exit(0); } // }}}

 

函数中,首先执行了各个模块的 exit_process,然后做了异常情况的检测,如果并非强制退出的情况下,仍然有未断开连接,则进入 ngx_debug_point 函数

 

// void ngx_debug_point(void) // 递送 SIGSTOP 信号给进程,让其停止运行,便于调试 {{{ void ngx_debug_point(void) { ngx_core_conf_t *ccf; ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, ngx_core_module); switch (ccf->debug_points) { case NGX_DEBUG_POINTS_STOP: raise(SIGSTOP); break; case NGX_DEBUG_POINTS_ABORT: ngx_abort(); } } // }}}

 

该函数递送 SIGSTOP 信号给进程,进程中止运行,保存现场等待调试

 

然后,销毁内存池,完成退出

 

进程收到 SIGTERM 或 SIGINT 信号后,会置位 ngx_terminate,从而执行 ngx_worker_process_exit 强制退出

与上述流程唯一不同的地方在于,这一过程不会去判断是否还有未断开连接,从而强制退出 worker 进程

 

进程收到 SIGUSR1 信号后,会置位 ngx_reopen,重新打开进程关联的所有日志文件

// 收到 SIGUSR1 信号后在 ngx_signal_handler 函数中置位 if (ngx_reopen) { ngx_reopen = 0; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs"); // 重新打开所有文件 ngx_reopen_files(cycle, -1); }

 

 

// void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user) // 重新打开所有日志文件 {{{ void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user) { ngx_fd_t fd; ngx_uint_t i; ngx_list_part_t *part; ngx_open_file_t *file; part = &cycle->open_files.part; file = part->elts; for (i = 0; /* void */ ; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; file = part->elts; i = 0; } if (file[i].name.len == 0) { continue; } if (file[i].flush) { file[i].flush(&file[i], cycle->log); } fd = ngx_open_file(file[i].name.data, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS); ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reopen file \"%s\", old:%d new:%d", file[i].name.data, file[i].fd, fd); if (fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_open_file_n " \"%s\" failed", file[i].name.data); continue; } #if !(NGX_WIN32) if (user != (ngx_uid_t) NGX_CONF_UNSET_UINT) { ngx_file_info_t fi; if (ngx_file_info((const char *) file[i].name.data, &fi) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_file_info_n " \"%s\" failed", file[i].name.data); if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_close_file_n " \"%s\" failed", file[i].name.data); } continue; } if (fi.st_uid != user) { if (chown((const char *) file[i].name.data, user, -1) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "chown(\"%s\", %d) failed", file[i].name.data, user); if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_close_file_n " \"%s\" failed", file[i].name.data); } continue; } } if ((fi.st_mode & (S_IRUSR|S_IWUSR)) != (S_IRUSR|S_IWUSR)) { fi.st_mode |= (S_IRUSR|S_IWUSR); if (chmod((const char *) file[i].name.data, fi.st_mode) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "chmod() \"%s\" failed", file[i].name.data); if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_close_file_n " \"%s\" failed", file[i].name.data); } continue; } } } if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "fcntl(FD_CLOEXEC) \"%s\" failed", file[i].name.data); if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_close_file_n " \"%s\" failed", file[i].name.data); } continue; } #endif if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_close_file_n " \"%s\" failed", file[i].name.data); } file[i].fd = fd; } (void) ngx_log_redirect_stderr(cycle); } // }}}

 

 






读书笔记      技术帖      linux      web      龙潭书斋      服务器      signal      信号      sigint      sigterm      nginx      server      worker      webserver      sigusr1     


京ICP备15018585号