php守护进程-Nginx源码分析:3张图了解启动和流程的工作原理

2023-08-26 0 342 百度已收录

陈克,拥有十年行业经验,曾在浙江电信、阿里巴巴、华为、五霸同城担任开发工程师和架构师。 目前负责合力家的前端架构和运维工作。 博客地址:

图1:nginx启动及显存申请流程分析

任何程序都离不开启动和配置分析。 ngx的代码离不开ngx_cycle_s和ngx_pool_s这两个核心数据结构,所以在开始之前我们先来分析一下。

内存申请过程分为3步

如果请求的显存大于当前块的剩余空间,则直接在当前块中分配。

如果当前块空间不足php守护进程,则调用ngx_palloc_block分配新块,并将新块链接到d.next,然后分配数据。

如果请求的大小小于当前块的最大值,则直接调用ngx_palloc_large分配一个大块并链接到pool→large链表

内存分配过程图如下

(图片来自网络)

为了更好的理解上图,可以参考文末附录2中的几个数据结构:ngx_pool_s和ngx_cycle_s。

知道了这两个核心数据结构之后,我们就要进入main函数了。 main函数的执行流程如下

初始化各个模块的索引并估算ngx_max_module;

调用ngx_init_cycle进行初始化

如果有信号,则进入ngx_signal_process进行处理;

调用ngx_init_signals初始化信号; 主要完成信号处理程序的注册;

如果没有继承的socket并且设置了守护进程标志,则调用ngx_daemon创建守护进程;

调用ngx_create_pidfile创建进程记录文件; (非NGX_PROCESS_MASTER = 1进程,不要创建此文件)

进入流程主循环;

在main函数执行过程中,有一个非常重要的函数ngx_init_cycle,这个阶段做了什么事情呢? 下面分析ngx_init_cycle,初始化过程:

更新时区和时间

创建内存池

将视频内存分配给循环指针

保存安装路径、配置文件、启动参数等。

初始化打开的文件句柄

初始化共享内存

初始化加入队列

保存主机名

调用每个NGX_CORE_MODULE的create_conf方法

解析配置文件

调用各个NGX_CORE_MODULE的init_conf方法

打开新文件句柄

创建共享内存

手柄攻丝插座

创建用于窃听的套接字

调用各个模块的init_module

图2:主流程工作原理及工作工程

以下流程均在ngx_master_process_cycle函数中进行启动进程:

暂时屏蔽ngx需要处理的所有信号

设置进程名称

启动工作进程

启动缓存管理进程

进入循环开始处理相关信号

主进程工作流程

设置worker进程退出的等待时间

挂断电话,等待新信号到来

更新时间

如果工作进程由于 SIGCHLD 信号而退出,则重新启动工作进程

master进程退出。如果所有worker进程退出并收到SIGTERM信号或SIGINT信号或SIGQUIT信号等,则master进程开始处理退出

处理 SIGTERM 信号

处理SIGQUIT信号并关闭套接字

处理 SIGHUP 信号

平滑升级,重启worker进程

升级不顺利,需要重新读取配置

进程重启 10 进程 SIGUSR1 信号重新打开所有文件 11 进程 SIGUSR2 信号热代码替换,执行新程序 12 进程 SIGWINCH 信号,不再处理任何请求

图3:worker进程的工作原理

首先执行 ngx_start_worker_processes 函数:

首先找到ngx_processes数组中的坑 if (ngx_processes[s].pid == -1) {break;}

流程相关结构初始化工作

创建管道(套接字对)

将管道设置为非阻塞模式

将管道设置为异步模式

设置异步I/O的所有者

如果执行exec,这个fd不会传递给exec创建的进程

fork 创建一个子进程。 创建成功后,子进程执行相关逻辑:proc(cycle, data)。

设置 ngx_processes[s] 相关属性

通知子进程新进程创建了 ngx_pass_open_channel(cycle, &ch);

接下来是ngx_worker_process_cycle工作进程逻辑

ngx_worker_process_init

初始化环境变量

设置进程优先级

设置文件句柄数量限制

设置 core_file 文件

用户组设置

CPU亲和性设置

设置工作目录

设置随机种子数

初始化窃听状态

调用各个模块的init_process方法进行初始化

关闭别人的fd[1],保留别人的fd[1],以便相互通信。 它自己的fd[1]接收来自主进程的消息。

收听频道阅读风暴

工艺模式

处理管道信号。 这个过程是由ngx_channel_handler完成的,这部分的具体实现在pipelinestorm中有解释。

线程模式

ngx_worker_thread_cycle是一个线程循环:不仅在无限循环中处理退出信号。 主要进行ngx_event_thread_process_posted的工作。 这个具体内容我们在讲风暴模型的时候会展开。

处理相关信号

Master和Worker之间的通信原理是:

Nginx事件机制简介

先看几个主要技能

n = 发送消息(s, &msg, 0);

表格顶部

表格底部

接下来分析storm模块的工作流程

ngx_event模块结构

ngx_events_module的数据结构如下:

ngx_module_t ngx_events_module = {

NGX_MODULE_V1,

&ngx_events_module_ctx, /* 模块上下文 */

ngx_events_commands, /* 模块指令 */

NGX_CORE_MODULE, /* 模块类型 */

, /* 初始化主机 */

, /* 初始化模块 */

, /* 初始化进程 */

, /* 初始化线程 */

, /* 退出线程 */

, /* 退出进程 */

, /* 退出主控 */

NGX_MODULE_V1_PADDING

};

ngx_event模块初始化

静态 ngx_command_t ngx_events_commands = {

ngx_string(“事件”) ,

NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS ,

ngx_events_block, 0, 0,

},

ngx__命令

};

通过ngx_events_commands数组,我们可以知道事件模块初始化函数为ngx_events_block,该函数的工作内容如下:

创建模块上下文结构

为所有 NGX_EVENT_MODULE 模块调用 create_conf

解析事件配置

为所有 NGX_EVENT_MODULE 模块调用 init_conf

ngx_core_event模块初始化

ngx_core_event_module 在 ngx_cycle_init 期间初始化:

for (i = 0; ngx_modules[i]; i++) {

if (ngx_modules[i]->init_module) {

if (ngx_modules[i]->init_module(cycle) != NGX_OK) { /* 致命 */

退出(1);

我们先看一下ngx_core_event_module的结构:

ngx_module_t ngx_event_core_module = {

NGX_MODULE_V1,

&ngx_event_core_module_ctx, /* 模块上下文 */

ngx_event_core_commands, /* 模块指令 */

NGX_EVENT_MODULE, /* 模块类型 */

, /* 初始化主机 */

ngx_event_module_init, /* 初始化模块 */

ngx_event_process_init, /* 初始化进程 */

, /* 退出主控 */ NGX_MODULE_V1_PADDING

};

ngx_event_module_init实现了初始化过程,分为以下步骤:

连接校准

初始化互斥锁

事件流程初始化

当工作线程初始化时,ngx_event_process_init将被调用:

for (i = 0; ngx_modules[i]; i++) {

if (ngx_modules[i]->init_process) {

if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) { /*致命 */

退出(2);

ngx_event_process_init 流程分为以下步骤:

设置 ngx_accept_mutex_held

初始化定时器

初始化真正的storm引擎(linux中的epoll)

初始化连接池

添加接受事件

ngx_process_events_and_timers 事件处理开始工作

工作流程如下:

ngx_trylock_accept_mutex只有在获得标志位后才注册接受事件。

ngx_process_events 处理风暴

释放accept_mutex锁

处理定时器混乱

ngx_event_process_posted 处理posted队列的扰动

ngx定时器实现

ngx的定时器是借助红黑树实现的

ngx 雷霆组处理

Accept_mutex解决了令人震惊的群体问题。 虽然linux的新内核已经解决了这个问题,但是ngx是为了兼容性。

整体示意图:

Nginx配置分析

让我们添加配置分析。 Nginx配置分析最大的亮点就是使用了五级指针与ctx关联,然后各个模块关注自己的配置,重点分析和初始化。

配置文件解析

ngx在main函数执行时会调用ngx_init_cycle。 在此过程中,会执行几个初始化步骤:

并根据模块号存放到cycle→conf_ctx中。 这个过程主要是初始化配置数据结构。 以epoll模块为例:

该函数总共有以下几个流程:

结构 ngx_conf_s {

字符*名称;

ngx_array_t *args;

ngx_cycle_t *循环;

ngx_pool_t *池;

ngx_pool_t *temp_pool;

ngx_conf_file_t *conf_file;

ngx_log_t *日志;

无效*ctx;

ngx_uint_t 模块类型;

ngx_uint_t cmd_type;

ngx_conf_handler_pt 处理程序;

字符 *handler_conf;

};

rv = ngx_conf_parse(cf, ) ; 初始化http上下文后,继续内部解析逻辑。 只有这样才会调用ngx_conf_handler下半部分的逻辑:

此阶段会根据配置项的值初始化核心模块。 ngx的配置结构如下:

整体结构

serv_conf结构

loc_conf结构

附件1:Nginx主要数据结构

我们可以参考ngx_connection_s结构体,该结构体存储了ngx_connection_s中数组的指针:ngx_queue_t队列

6.ngx_hash_t

ngx的哈希表没有数组,如果找不到,就会继续向右寻找空闲的桶。 ngx_hash_init的整体初始化流程为:

预计需要的桶数

搜索所需的桶数

分配桶显存

初始化每个ngx_hash_elt_t

ngx对于显存有专门的扣除,假设哈希表不会占用太多的数据和空间,所以采用了这种方法。

附录2:内存分配的数据结构

ngx_pool_s是ngx的内存池php守护进程,每个工作线程都会持有一个,我们看一下它的结构:

结构 ngx_pool_s {

ngx_pool_data_t d ; // 数据块

最大尺寸; // 小显存的最大值

ngx_pool_t *当前; // 指向当前内存池

ngx_chain_t *链;

ngx_pool_large_t *大; // 分配大块显存,即显存请求超过max

ngx_pool_cleanup_t *清理; // 当某些内存池被挂载和释放时,资源同时释放

ngx_log_t *日志;

};

ngx_pool_data_t数据结构:

类型定义结构{

u_char *最后; // 当前数据块分配的结束位置

u_char *结束; // 数据块结束位置

ngx_pool_t *下一个; // 链接到下一个内存池

ngx_uint_t 失败; // 统计内存池无法满足分配请求的次数

} ngx_pool_data_t;

那么我们结合ngx_palloc方法来看看内存池的分配原理:

void * ngx_palloc (ngx_pool_t *pool, size_t 大小) {

u_char *m; ngx_pool_t *p;

如果(最大尺寸){

p = 池->当前;

做 {

m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT) ;

if ((size_t) (p->d.end - m) >= size) {

p->d.last = m + 大小;

返回米;

p = p->d.下一个;

而(p);

返回 ngx_palloc_block(池, 大小) ;

返回 ngx_palloc_large(池, 大小) ;

每个工作进程维护一个 ngx_cycle_s:

结构 ngx_cycle_s {

无效****conf_ctx; // 配置上下文列表(包括所有模块)

ngx_pool_t *池; // 内存池

ngx_log_t *日志; // 日志

ngx_log_t new_log;

ngx_connection_t **文件; // 连接文件

ngx_connection_t *自由连接; // 空闲连接

ngx_uint_t free_connection_n ; // 空闲连接数

ngx_queue_t reusable_connections_queue ; // 再次使用连接队列

ngx_array_t 监听; // 监听列表

ngx_array_t 路径; // 路径链表

ngx_list_t open_files ; // 打开文件数组

ngx_list_t 共享内存; // 共享内存链表

ngx_uint_t 连接_n ; // 连接数

ngx_uint_tiles_n ; // 打开文件数

ngx_connection_t *连接; // 连接

ngx_event_t *read_events; // 读取事件

ngx_event_t *write_events; // 写入事件

ngx_cycle_t *old_cycle; //旧循环指针

ngx_str_t conf_file; //配置文件

ngx_str_t conf_param; //配置参数

ngx_str_t conf_prefix; //配置前缀

ngx_str_t 前缀; //字首

ngx_str_t 锁文件; //锁定文件

ngx_str_t 主机名; //主机名

};

附录3:Nginx内存管理和内存对齐

内存申请最终调用malloc函数,ngx_calloc调用ngx_alloc后使用memset填充0。 如果自己开发NGX模块,不要直接使用ngx_malloc/ngx_calloc,可以使用ngx_palloc,否则需要自己管理显存的释放。 ngx_http_create_request期间将创建请求级池:

池 = ngx_create_pool(cscf->request_pool_size, c->log) ;

如果(池==){

返回;

r = ngx_pcalloc(池, sizeof(ngx_http_request_t));

如果(r==){

ngx_destroy_pool(池);

r->池=池;

当 ngx_http_free_request 释放请求时,会调用 ngx_destroy_pool ( pool ) 释放连接。 内存对齐,首先在创建池时对齐:p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log)。 ngx_memalign(根据指定的对齐方式返回大小的显存空间,其地址为对齐方式的整数倍,对齐方式为2的幂。)最后通过:posix_memalign或memalign来申请。

数据对齐(alignment)是指由硬件条件决定的数据的地址与内存块的大小之间的关系。 当变量的地址是其大小的倍数时,称为自然对齐。 例如,对于一个32位变量,如果它的地址是4的倍数——也就是说,如果地址的低两位是0,那么这就是自然对齐。 所以,如果一个类型的大小是2n字节,那么它的地址至少低n位是0。对齐规则是由硬件决定的。 某些计算机体系结构对数据对齐有非常严格的要求。 在某些系统上,未对齐的数据加载可能会导致进程深度陷入困境。 在其他系统上,访问未对齐的数据是安全的,但会导致性能提高。 编写可移植代码时必须防止对齐问题,所有类型都应该自然对齐。

预对齐视频内存的分配 在大多数情况下,编译器和 C 库会透明地为您处理对齐问题。 POSIX 指定 malloc、calloc 和 realloc 返回的地址对于任何 C 类型都是对齐的。 在 Linux 中,这些函数返回的地址在 32 位系统上按 8 字节边界对齐,在 64 位系统上按 16 字节边界对齐。 有时,对于较大的边界(例如页面),程序员需要动态对齐。 虽然动机各不相同,但最常见的是直接块 I/O 或其他软件到硬件交互的缓存对齐,因此 POSIX 1003.1d 提供了一个名为 posix_memalign 的函数。

当调用posix_memalign成功时,会返回size字节的动态显存,该显存的地址是对齐的倍数。 对齐参数必须是 2 的幂,或者 void 指针大小的倍数。 返回的内存块的地址放在memptr中,函数返回值为0。

指针对齐: #define ngx_align_ptr(p, a) (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))

例如:计算宏ngx_align(1, 64) = 64,只要输入d < 64,结果总是64,如果输入d = 65,结果就是128,以此类推。

管理内存池时,对于大于64字节的显存,分配64字节,使其始终是cpu二级缓存读写行大小的倍数,有利于速度和效率CPU二级缓存。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 php php守护进程-Nginx源码分析:3张图了解启动和流程的工作原理 https://www.wkzy.net/game/164739.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务