Nginx源码分析——master进程与worker进程(二)
2021/9/7 7:07:54
本文主要是介绍Nginx源码分析——master进程与worker进程(二),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
一、说明
在前面一章,我们大致了解了nginx进程的启动,没有深入的去看进程到底做做了些什么事情,本章我们就看看master进程工作到底在做什么。二、函数分析
./src/os/unix/ngx_process_cycle.c>ngx_master_process_cycle(ngx_cycle_t *)ngx_new_binary = 0; delay = 0; sigio = 0; live = 1; for ( ;; ) { if (delay) { if (ngx_sigalrm) { sigio = 0; delay *= 2; ngx_sigalrm = 0; } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "termination cycle: %M", delay); 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) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setitimer() failed"); } } ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend"); sigsuspend(&set); ngx_time_update(); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "wake up, sigio %i", sigio); 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_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; } if (ngx_quit) { ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); ngx_close_listening_sockets(cycle); continue; } 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; } ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring"); cycle = ngx_init_cycle(cycle); if (cycle == NULL) { cycle = (ngx_cycle_t *) ngx_cycle; continue; } ngx_cycle = cycle; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_JUST_RESPAWN); ngx_start_cache_manager_processes(cycle, 1); /* allow new processes to start */ ngx_msleep(100); live = 1; ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); } if (ngx_restart) { ngx_restart = 0; ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); ngx_start_cache_manager_processes(cycle, 0); live = 1; } 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)); } if (ngx_change_binary) { ngx_change_binary = 0; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary"); ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv); } if (ngx_noaccept) { ngx_noaccept = 0; ngx_noaccepting = 1; ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); } } 老规矩,还是一段一段分析吧,不过这块代码看起来还是比较好懂的。 if (delay) { if (ngx_sigalrm) { sigio = 0; delay *= 2; ngx_sigalrm = 0; } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "termination cycle: %M", delay); 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) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setitimer() failed"); } }setitimer是指经过指定时间之后,内核将发送SIGALRM信号给本进程。指定的时间就是itv。接收到指令之后,ngx_sigalrm就会变成1,当delay不为0的时候,会不断的翻倍。delay用来等待worker进程退出的时间,master进程接收到信号之后,会发信号给worker进程进行退出,worker进程退出需要时间,delay就是等待的时间。
sigsuspend(&set);这个时候master进程会被挂起,当进程接受到新的信号之后,将会返回继续只是,同时将这个信号完成。上文提到过,处理与发起信号是通过ngx_init_signals执行ngx_signal_handler中的SIGALRM信号。ngx_signal_handler函数中就会将根据输入 设置下面的状态
ngx_time_update();
master进程处理信息的时候,更新缓存时间。
if (ngx_reap) { ngx_reap = 0; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children"); live = ngx_reap_children(cycle); }在上面提到过,进程挂起的时候,会监听对应的信号,这里当收到SIGCHLD信号的时候,进入这个逻辑。SIGCHLD信号是在子进程终止的时候向父进程发的信号。
ngx_reap_children函数会遍历ngx_process数组,检查每一个子进程的状态,对于异常退出的进程重新拉起。只有当子进程全部正常退出的时候,live才会是0,其他的时候返回的live为1。意思就是如果是异常退出,这里会将进程重新拉起,如果是有接收到退出命令的,那么就正常退出。
if (!live && (ngx_terminate || ngx_quit)) { ngx_master_process_exit(cycle); }如果live为0就是子进程全部正常退出,并且根据输入的命令,如果是stop,就执行退出master进程的操作。
master退出的时候,会将pid的文件删掉,释放链接、释放cycle对象里面的内存空间、删除监听的事件等等,并且发出信号。
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; }
进行终止的时候,之前提到先设置delay超时时间,如果没有超时就执行
ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_TERMINATE_SIGNAL)); 发出TERM信号,正常退出worker进程 如果退出超时就执行,强制退出 ngx_signal_worker_processes(cycle, SIGKILL); 并且通过stop关闭的时候,就不关系是不是还有请求在处理,就直接关闭了,这样就会有quit命令。//TODO ngx_signal_worker_processes函数做了什么后面去填这个坑,至于什么时候。。随缘吧。。if (ngx_quit) { ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); ngx_close_listening_sockets(cycle); continue; }这个就是quit命令,相对上面就是没有业务之后,才会去关闭socket,比较优雅。
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; } ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring"); cycle = ngx_init_cycle(cycle); if (cycle == NULL) { cycle = (ngx_cycle_t *) ngx_cycle; continue; } ngx_cycle = cycle; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_JUST_RESPAWN); ngx_start_cache_manager_processes(cycle, 1); /* allow new processes to start */ ngx_msleep(100); live = 1; ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); }这个对应的就是reload命令,重新读取配置文件。
ngx_new_binary这个标志位先不去看,直接看下面的。后续的动作就比较明显了,初始化ngx_cycle_t结构体,重新读取配置文件。启动新的worker进程跟Cache进程,然后等待启动完成,然后退去原来老的worker进程。 这里先不按照代码顺序讲,先说ngx_change_binary状态
if (ngx_change_binary) { ngx_change_binary = 0; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary"); ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv); }处理的信号是USR2。 nginx提供了平滑升级的能力。平滑启动之后,ngx_new_binary中保存的就是pid。所以上面的reload的时候ngx_new_binary判定是执行不到的。
if (ngx_restart) { ngx_restart = 0; ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); ngx_start_cache_manager_processes(cycle, 0); live = 1; }
这个简单就是重启,没有对应的信号,就是在处理逻辑里面设置了ngx_restart就进行处理。
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)); }就是对应的命令reopen,重新设置日志文件。
if (ngx_noaccept) { ngx_noaccept = 0; ngx_noaccepting = 1; ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); }
所有的进程不在接收请求,进行从容关闭进程。
三、总结
这块很多要去解决的问题就是进程之前信号通信的问题,之前对这块也不是特别的了解,所有很多都是一知半解,也希望后面对这块有更深的了解。这篇关于Nginx源码分析——master进程与worker进程(二)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-13用Nginx防范DDoS攻击的那些事儿
- 2024-12-13用Terraform在AWS上搭建简单NGINX服务器指南
- 2024-10-29Nginx发布学习:从入门到实践的简单教程
- 2024-10-28Nginx发布:新手入门教程
- 2024-10-21nginx 怎么设置文件上传最大20M限制-icode9专业技术文章分享
- 2024-10-17关闭 nginx的命令是什么?-icode9专业技术文章分享
- 2024-09-17Nginx实用篇:实现负载均衡、限流与动静分离
- 2024-08-21宝塔nginx新增8022端口方法步骤-icode9专业技术文章分享
- 2024-08-21nginx配置,让ws升级为wss访问的方法步骤-icode9专业技术文章分享
- 2024-08-15nginx ws代理配置方法步骤-icode9专业技术文章分享