nginx的进程模式
使用 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.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
{ 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); } } }
{ 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); } } } }
{ 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; } } } }
if (ngx_signal) { return ngx_signal_process(cycle, ngx_signal); }从nginx.pid文件中解析出当前的 master 进程PID, 向其发送 reload 信号
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) {} }
{ /* 接受到 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; } }
{ 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 置为有效. */ } } } }
{ ngx_cache_manager_process_cycle() { //... for (;;) { if (ngx_terminate || ngx_quit) { exit(0); } } } }
if (ngx_signal) { return ngx_signal_process(cycle, ngx_signal); }从nginx.pid文件中解析出当前的 master 进程PID, 向其发送 stop 信号
{ /* 接收信号 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; } } } }
{ 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); } } }
if (ngx_signal) { return ngx_signal_process(cycle, ngx_signal); }
{ // 接收信号 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; } } } }
{ 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); } } } }
if (ngx_signal) { return ngx_signal_process(cycle, ngx_signal); }从nginx.pid文件中解析出当前的 master 进程PID, 向其发送 reopen 信号
{ // 接收信号 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)); } } } }
{ 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); } } }
{ 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; } }
2> 接收ngx_reap_children()创建新进程时, 使用UNIX域套接字发送来的 NGX_CMD_OPEN_CHANNEL;执行关闭对应进程的 UNIX域套接字,清理pid及所在进程池的位置.
----end-----
From: GS