nginx master 进程主流程
nginx master 进程主流程
之前有说到 nginx 进程模型-整体架构,下面来看一下 nginx master
进程的主要工作
nginx
的入口 main
函数在 nginx.c
文件中
函数原型为:
int ngx_cdecl main(int argc, char *const *argv)
在这个函数中,master
做了一系列的初始化操作
最终在下面这个地方进入了主流程中:
//... if (ngx_process == NGX_PROCESS_SINGLE) { ngx_single_process_cycle(cycle); } else { ngx_master_process_cycle(cycle); } //...
因为我们主要看 master-worker
这种进程模型,所以进入 ngx_master_process_cycle
设置信号屏蔽字,防止创建子进程过程中被信号中断
// 先清空信号集 sigemptyset(&set); sigaddset(&set, SIGCHLD); sigaddset(&set, SIGALRM); sigaddset(&set, SIGIO); sigaddset(&set, SIGINT); sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL)); // 设置信号屏蔽字,将 set 中的信号设置为阻塞状态,防止创建worker 的过程中,被进来的信号打断 if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "sigprocmask() failed"); } // 将 set 清空 sigemptyset(&set);
设置信号屏蔽字,防止在创建子进程的过程中被信号处理程序中断
关于信号屏蔽字,引用 《UNIX 环境高级编程》
中信号一节的部分内容:
进程可以选用
“阻塞信号递送”
。如果为进程产生了一个阻塞
的信号,而且对该信号的动作是系统默认动作或捕捉该信号, 则为该进程将此信号保持为未决状态
,直到该进程对此信号解除了阻塞, 或者将对此信号的动作更改为忽略。内核在递送
一个原来被阻塞的信号给进程时(而不是在产生该信号时),才决定对它的处理方式。于是进程在信号递送给它之前仍可改变对该信号的动作。进程调用sigpending
函数(见10.13节)来判定哪些信号是设置为阻塞并处于未决状态的。
每个进程都有一个
信号屏蔽字( signal mask)
,它规定了当前要阻塞递送到该进程的信号集
。对于每种可能的信号,该屏蔽字中都有一位与之对应。对于某种信号,若其对应位已设置,则它当前是被阻塞的。进程可以调用sigprocmask
(在10.12节中说明)来检测和更改其当前信号屏蔽字。
当然,在下面创建完子进程之后,会使用 sigsuspend
解除信号屏蔽,并使 master 进程进入休眠
关于 sigsuspend
函数,简单来说,它是一个 sigprocmask(SIG_SETMASK, &emptyset, NULL)
和 pause()
函数的结合体,不过相对于使用两个函数完成上述操作,sigsuspend
是 原子操作
。
具体的操作如下:
- 使用新的信号集合设置屏蔽字,在这里是清空屏蔽字
- 调用信号处理函数,并从信号处理程序返回
- 屏蔽字恢复成调用
sigsuspend
之前的值(再次不让进程被信号打断)
设置 master 进程的 title
static u_char master_process[] = "master process"; size = sizeof(master_process); for (i = 0; i < ngx_argc; i++) { size += ngx_strlen(ngx_argv[i]) + 1; } title = ngx_pnalloc(cycle->pool, size); if (title == NULL) { /* fatal */ exit(2); } p = ngx_cpymem(title, master_process, sizeof(master_process) - 1); for (i = 0; i < ngx_argc; i++) { *p++ = ' '; p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size); } ngx_setproctitle(title);
将进程title
设置成三个部分:
- 固定字符:
nginx:
- 主进程标志:
master process
- 命令行启动的命令,如:
/home/ubuntu/mydisk/var/nginx-debug-1/sbin/nginx
所以进程刚开始是这样显示的:
$ ps aux | grep nginx | grep -v grep root 183117 0.0 0.0 4324 2680 ? ts 17:07 0:00 /home/ubuntu/mydisk/var/nginx-debug-1/sbin/nginx
设置了 title 之后,变成了下面这样:
$ ps aux | grep nginx | grep -v grep root 183117 0.0 0.0 4324 2680 ? ts 17:07 0:00 nginx: master process /home/ubuntu/mydisk/var/nginx-debug-1/sbin/nginx
根据配置启动相应数量的 worker
和 cache
管理进程
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); ngx_start_cache_manager_processes(cycle, 0);
监听信号,并作出响应
在 ngx_init_signals
函数中,对原始信号做了变量名映射,具体映射如下:
信号 | 对应进程中的标志位变量 | 含义 |
---|---|---|
QUIT | ngx_quit | 优雅关闭服务 |
TERM 或 INT | ngx_terminate | 强制关闭服务 |
USR1 | ngx_reopen | 重新打开服务中的所有文件 |
WINCH | ngx_noaccept | 所有子进程不再接受处理新的连接,实际相当于对所有的子进程发送 QUIT 信号 |
USR2 | ngx_change_binary | 平滑升级到新版本的 Nginx 程序 |
HUP | ngx_reconfigure | 重新读取配置文件并使服务对新配置项生效 |
CHLD | ngx_reap | 有子进程意外结束 master 会监控所有子进程,并在子进程意外退出时调用 ngx_reap_children 方法重启子进程 |
master 并不是时刻不停的执行循环检测这些标志位,而是通过 sigsuspend
进入休眠,等待有信号唤醒进程时,再循环检测所有信号并处理。
参考
《UNIX 环境高级编程》
本文来自博客园,作者:吴丹阳-V,转载请注明原文链接:https://www.cnblogs.com/wudanyang/p/14695028.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2020-04-23 使用 MWeb + Typora 写作并发布到博客园【过时】