在 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 进程处理的信号信号 | 全局变量 | 意义 |
SIGQUIT | ngx_quit | 关闭进程 |
SIGTERM SIGINT | ngx_terminate | 强制退出 |
USR1 | ngx_reopen | 重新打开所有文件 |
WHICH | ngx_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