Nginx源码分析 - 主流程篇 - 模块的初始化(12)

深入理解Nginx线程池【内附原理讲解以及源码分析】 本篇博客将从什么是并发编程、为什么要使用线程池、线程池的组成以及线程池的使用这4个方面对Nginx线程池进行刨析,相信耐心看完会对您有所帮助。 阅读详情
AI摘要

本文介绍Nginx模块初始化过程,包括模块结构定义、模块数组ngx_modules的生成、模块编号处理、模块内存分配及初始化等关键步骤。

目录

一、模块的数据结构

1. ngx_module_s 模块结构

2. ngx_core_module 核心模块

二、模块的初始化

1. 模块编号处理ngx_preinit_modules

2. 初始化cycle->modules 分配内存

3. 每个模块进行初始化ngx_init_modules

4. 统计有多少个模块ngx_count_modules

5. 模块的工作进程初始化init_process


Nginx是高度模块化的,各个功能都会封装在模块中。例如core模块、HTTP模块等。也可以自定义模块。

这一篇文章主要讲解模块的初始化。后续会有一篇文章教你如何编写Nginx的模块。

Nginx实现模块管理的代码主要在:/src/core/ngx_module.c文件中。

一、模块的数据结构


1. ngx_module_s 模块结构


结构体ngx_module_s主要用于管理每一个模块的详细信息。

Nginx的所有模块会放置在全局变量cycle的cycle->modules 模块数组。通过这个数组,我们就可以拿到每个模块的具体信息。

/**
 * 业务模块数据结构
 */
struct ngx_module_s {
    ngx_uint_t            ctx_index;
    ngx_uint_t            index; /* 模块的唯一标识符号 */

    char                 *name;  /* 模块名称 */

    ngx_uint_t            spare0;
    ngx_uint_t            spare1;

    ngx_uint_t            version;	/* 模块版本 */
    const char           *signature;

    void                 *ctx;	/* 模块上下文 */
    ngx_command_t        *commands; /* 模块支持的命令集 */
    ngx_uint_t            type;	/* 模块类型 */

    /* 回调函数 */
    ngx_int_t           (*init_master)(ngx_log_t *log); /* 主进程初始化的时候调用 */

    ngx_int_t           (*init_module)(ngx_cycle_t *cycle); /* 模块初始化的时候调用 */

    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);  /* 工作进程初始化时调用*/
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle); /* 线程初始化调用*/
    void                (*exit_thread)(ngx_cycle_t *cycle);	/* 线程退出调用*/
    void                (*exit_process)(ngx_cycle_t *cycle); /* 工作进程退出调用*/

    void                (*exit_master)(ngx_cycle_t *cycle);

    uintptr_t             spare_hook0;
    uintptr_t             spare_hook1;
    uintptr_t             spare_hook2;
    uintptr_t             spare_hook3;
    uintptr_t             spare_hook4;
    uintptr_t             spare_hook5;
    uintptr_t             spare_hook6;
    uintptr_t             spare_hook7;
};

说明:

1. index 主要用于模块的标识。

  • cycle->conf_ctx主要存储的是各个模块的配置文件结构的指针地址。
  • cycle->conf_ctx中获取各个模块配置信息都是通过模块的标识来确定数组位置的。

例如核心模块是放在一个ngx_core_conf_t的数据结构上的。而ngx_core_conf_t这个指针就通过index索引值放在cycle->conf_ctx数组中。

获取核心模块的配置信息:

  ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

2. commands主要用于配置模块的命令集。Nginx的配置文件都是通过commands命令集来逐个解析具体定义好的配置信息(每个模块不一样)。下一章会详解解读

3. ctx 模块上下文。主要放置一个模块自定义的结构。例如核心模块就是ngx_core_module_t的结构。ngx_core_module_t中可以自定义一些方法或者参数。

4. type。模块类型。

5. init_module:初始化模块的时候会回调的函数。

2. ngx_core_module 核心模块


  • 核心模块在nginx.c的头部就定义了。定义了 ngx_core_module核心模块和核心模块的上下文ngx_core_module_ctx
  • ngx_core_module_t 为核心模块的上下文结构。主要用于核心模块的配置文件创建ngx_core_module_create_conf和初始化ngx_core_module_init_conf。放置在ngx_module_s->ctx。
/**
 * 核心模块core数据结构
 * ngx_module_s->ctx 核心模块的上下文,主要定义了创建配置和初始化配置的结构
 */
typedef struct {
    ngx_str_t             name;
    void               *(*create_conf)(ngx_cycle_t *cycle);
    char               *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;

 

/**
 * 核心模块配置文件
 * ngx_core_module_create_conf 核心模块创建配置文件
 * ngx_core_module_init_conf 核心模块初始化配置文件
 */
static ngx_core_module_t  ngx_core_module_ctx = {
    ngx_string("core"),
    ngx_core_module_create_conf,
    ngx_core_module_init_conf
};

/**
 * 核心模块
 */
ngx_module_t  ngx_core_module = {
    NGX_MODULE_V1,
    &ngx_core_module_ctx,                  /* module context */
    ngx_core_commands,                     /* module directives */
    NGX_CORE_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

二、模块的初始化


1. 模块编号处理ngx_preinit_modules


在nginx.c的main函数中,第一步对模块的处理就是进行编号处理。仅仅是编号,不做任何其他处理。

nginx.c:

    /* 初始化所有模块;并对所有模块进行编号处理;
     * ngx_modules数却是在自动编译的时候生成的,位于objs/ngx_modules.c文件中   */
    if (ngx_preinit_modules() != NGX_OK) {
        return 1;
    }

ngx_module.c 具体函数实现

/**
 * 初始化所有模块;并对所有模块进行编号处理;
 */
ngx_int_t
ngx_preinit_modules(void)
{
    ngx_uint_t  i;

    for (i = 0; ngx_modules[i]; i++) {
        ngx_modules[i]->index = i;
        ngx_modules[i]->name = ngx_module_names[i];
    }

    ngx_modules_n = i;
    ngx_max_module = ngx_modules_n + NGX_MAX_DYNAMIC_MODULES;

    return NGX_OK;
}

我们可以看到,模块的个数是通过ngx_modules的数组得到的。

ngx_modules是一个引用外部的变量。在ngx_modules.h中

/* 模块数组,所有的模块都会保存在此数组中   共有四种类型模块:"CORE","CONF","EVNT","HTTP" */
extern ngx_module_t  *ngx_modules[];

而ngx_modules的模块到底是什么时候确定的呢?

  • 具体的模块可通过编译前的configure命令进行配置,即设置哪些模块需要编译,哪些不被编译。当编译的时候,会生成ngx_modules.c的文件,里面就包含模块数组。
  • 新增模块或者减少模块可以在configure命令执行前 auto/modules文件里面修改。

生成的objs/ngx_modules.c文件如下:

00001:
00002: #include <ngx_config.h>
00003: #include <ngx_core.h>
00004:
00005:
00006:
00007: extern ngx_module_t ngx_core_module;
00008: extern ngx_module_t ngx_errlog_module;
00009: extern ngx_module_t ngx_conf_module;
00010: extern ngx_module_t ngx_events_module;
00011: extern ngx_module_t ngx_event_core_module;
00012: extern ngx_module_t ngx_epoll_module;
00013: extern ngx_module_t ngx_http_module;
00014: extern ngx_module_t ngx_http_core_module;
00015: extern ngx_module_t ngx_http_log_module;
00016: extern ngx_module_t ngx_http_upstream_module;
00017: extern ngx_module_t ngx_http_static_module;
00018: extern ngx_module_t ngx_http_autoindex_module;
00019: extern ngx_module_t ngx_http_index_module;
00020: extern ngx_module_t ngx_http_auth_basic_module;
00021: extern ngx_module_t ngx_http_access_module;
00022: extern ngx_module_t ngx_http_limit_zone_module;
00023: extern ngx_module_t ngx_http_limit_req_module;
00024: extern ngx_module_t ngx_http_geo_module;
00025: extern ngx_module_t ngx_http_map_module;
00026: extern ngx_module_t ngx_http_split_clients_module;
00027: extern ngx_module_t ngx_http_referer_module;
00028: extern ngx_module_t ngx_http_rewrite_module;
00029: extern ngx_module_t ngx_http_proxy_module;
00030: extern ngx_module_t ngx_http_fastcgi_module;
00031: extern ngx_module_t ngx_http_uwsgi_module;
00032: extern ngx_module_t ngx_http_scgi_module;
00033: extern ngx_module_t ngx_http_memcached_module;
00034: extern ngx_module_t ngx_http_empty_gif_module;
00035: extern ngx_module_t ngx_http_browser_module;
00036: extern ngx_module_t ngx_http_upstream_ip_hash_module;
00037: extern ngx_module_t ngx_http_stub_status_module;
00038: extern ngx_module_t ngx_http_write_filter_module;
00039: extern ngx_module_t ngx_http_header_filter_module;
00040: extern ngx_module_t ngx_http_chunked_filter_module;
00041: extern ngx_module_t ngx_http_range_header_filter_module;
00042: extern ngx_module_t ngx_http_gzip_filter_module;
00043: extern ngx_module_t ngx_http_postpone_filter_module;
00044: extern ngx_module_t ngx_http_ssi_filter_module;
00045: extern ngx_module_t ngx_http_charset_filter_module;
00046: extern ngx_module_t ngx_http_userid_filter_module;
00047: extern ngx_module_t ngx_http_headers_filter_module;
00048: extern ngx_module_t ngx_http_copy_filter_module;
00049: extern ngx_module_t ngx_http_range_body_filter_module;
00050: extern ngx_module_t ngx_http_not_modified_filter_module;
00051:

2. 初始化cycle->modules 分配内存


主要在cycle->modules上分配一块用于存放ngx_module_s数据结构的列表内存。并且将原来的 ngx_modules拷贝到cycle->modules上。

ngx_cycle.c

    /* 创建模块以及创建模块的配置信息 */
    if (ngx_cycle_modules(cycle) != NGX_OK) {
        ngx_destroy_pool(pool);
        return NULL;
    }

ngx_modules.c

/**
 * ngx_init_cycle 在初始化cycle的时候,初始化模块
 * 创建一个列表,并将静态的模块拷贝到列表上
 */
ngx_int_t
ngx_cycle_modules(ngx_cycle_t *cycle)
{
    /*
     * create a list of modules to be used for this cycle,
     * copy static modules to it
     */

    cycle->modules = ngx_pcalloc(cycle->pool, (ngx_max_module + 1)
                                              * sizeof(ngx_module_t *));
    if (cycle->modules == NULL) {
        return NGX_ERROR;
    }

    ngx_memcpy(cycle->modules, ngx_modules,
               ngx_modules_n * sizeof(ngx_module_t *));

    cycle->modules_n = ngx_modules_n;

    return NGX_OK;
}

3. 每个模块进行初始化ngx_init_modules


  • ngx_module_s结构中定义了init_module的模块初始化回调函数。ngx_init_modules主要用于每个模块的初始化工作。
  • 在编写自定义模块的时候,可以定义init_module方法,主要用于这个模块的初始化工作。

ngx_cycle.c

    /* 调用每个模块的初始化函数 */
    if (ngx_init_modules(cycle) != NGX_OK) {
        /* fatal */
        exit(1);
    }

真正的各个模块初始化 ngx_modules.c:

/**
 * 对每个模块进行一次初始化操作
 * 调用 init_module 回调函数,初始化每个模块的数据
 */
ngx_int_t
ngx_init_modules(ngx_cycle_t *cycle)
{
    ngx_uint_t  i;

    for (i = 0; cycle->modules[i]; i++) {
        if (cycle->modules[i]->init_module) {
            if (cycle->modules[i]->init_module(cycle) != NGX_OK) {
                return NGX_ERROR;
            }
        }
    }

    return NGX_OK;
}

4. 统计有多少个模块ngx_count_modules


/**
 * 统计每个类型下面总共多少个模块
 */
ngx_int_t
ngx_count_modules(ngx_cycle_t *cycle, ngx_uint_t type)
{
    ngx_uint_t     i, next, max;
    ngx_module_t  *module;

    next = 0;
    max = 0;

    /* count appropriate modules, set up their indices */

    for (i = 0; cycle->modules[i]; i++) {
        module = cycle->modules[i];

        if (module->type != type) {
            continue;
        }

        if (module->ctx_index != NGX_MODULE_UNSET_INDEX) {

            /* if ctx_index was assigned, preserve it */

            if (module->ctx_index > max) {
                max = module->ctx_index;
            }

            if (module->ctx_index == next) {
                next++;
            }

            continue;
        }

        /* search for some free index */

        module->ctx_index = ngx_module_ctx_index(cycle, type, next);

        if (module->ctx_index > max) {
            max = module->ctx_index;
        }

        next = module->ctx_index + 1;
    }

    /*
     * make sure the number returned is big enough for previous
     * cycle as well, else there will be problems if the number
     * will be stored in a global variable (as it's used to be)
     * and we'll have to roll back to the previous cycle
     */

    if (cycle->old_cycle && cycle->old_cycle->modules) {

        for (i = 0; cycle->old_cycle->modules[i]; i++) {
            module = cycle->old_cycle->modules[i];

            if (module->type != type) {
                continue;
            }

            if (module->ctx_index > max) {
                max = module->ctx_index;
            }
        }
    }

    /* prevent loading of additional modules */

    cycle->modules_used = 1;

    return max + 1;
}

5. 模块的工作进程初始化init_process


  • 在nginx_process_cycle.c文件的ngx_worker_process_init方法(Nginx是多进程模式的,此方法是每个进程进行自己的初始化工作)中包含模块的进程初始化。
  • 模块进程初始化,主要回调模块数据结构中的init_process回调函数。

此方法,会在后面的章节中详细讲解。

	/* 对模块进程初始化 - 这边初始化的是所有的模块有init_process回调函数的进行初始化工作 */
	for (i = 0; cycle->modules[i]; i++) {
		if (cycle->modules[i]->init_process) {
			if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {
				/* fatal */
				exit(2);
			}
		}
	}

 

Nginx底层设计与源码分析(未完) Nginx(发音为“engine-x”)是一个免费、开源、高性能的Web服务器和反向代理软件。它被开发用于处理高流量网站并作为负载均衡器、内容缓存和Web服务器。Nginx旨在轻巧高效,专注于可伸缩性和性能。它采用事件驱动、异步、非阻塞的架构,能高效地同时处理多个连接。这使它成为高流量网站和应用程序的热门选择。Nginx可用于提供静态文件,也可将请求代理到其他Web服务器,如Apache或Tomcat。它还可以用作负载均衡器,将传入流量分布到多个服务器上。 阅读详情

相关推荐

nginx源码分析 (信号机制)

这篇文章讲解nginx的信号机制,因为是信号,所以其他方面的没有深究,放在后面分享出来,欢迎有兴趣的朋友留言交流 nginx运行的进程模式为master-worker的话,那master和worker进程间是通过信号来通信,关于信号相关的知识可以参考下面的链接Linux信号(signal) 机制分析 下面讲解代码执行流程都是以quit命令来执行的 一、信号的安装 nginx启动的过程中,m...

pythonluo的专栏 732

Nginx源码分析(25篇)

Nginx源码分析 - 初探Nginx的架构 Nginx源码分析 - 基础数据结构篇 - 内存池 ngx_palloc.c Nginx源码分析 - 基础数据结构篇 - 数组结构 ngx_array.c Nginx源码分析 - 基础数据结构篇 - 缓冲区结构 ngx_buf.c Nginx源码分析 - 基础数据结构篇 - 双向链表结构 ngx_queue.c Nginx源码分析 ......

奇华资料的博客 9万+

nginx stream proxy 模块的ssl连接源码分析

本为对 nginx的ngx_stream_proxy_module与上游服务器建立ssl连接的过程进行了详细分析。

一个致力于开源代码学习、分析和交流的博客 3331

nginx源码分析—模块及其初始化

本博客(http://blog.youkuaiyun.com/livelylittlefish)贴出作者(阿波)相关研究、学习内容所做的笔记,欢迎广大朋友指正!Content0. 序1. nginx有哪些模块?2. nginx如何描述这些模块?2.1 模块数据结构2.1.1 ngx_module_t结构2.1.2 ngx_command_t结构2.2 模块类图3. nginx如何组织这些模块?3.1 全局数组

学习,思考,记录,分享。 4万+

nginx源码分析(2)——http模块的初始化过程

前一篇文章介绍了nginx的启动初始化过程,包括了所有模块的初始化过程,比如http模块、事件模块等。这里再详细介绍一下http模块的启动过程,还记得在前一篇文章中提到过ngx_conf_parse函数背后隐藏了大量的细节吗?这里就揭开这层神秘的面纱,去看看几个重要的http模块是如何初始化的。这里依然沿用上一篇文章的结构,首先来看几个重要的数据结构。 1. 重要的数据结构         1

架构师专栏 1万+

nginx源码分析(5)——监听socket初始化

在nginx源码分析(4)中,看到了nginx的事件模型,但其中没有介绍监听socket的初始化。而对于web server来说,需要通过监听socket来监听客户端的连接等。本篇将会具体介绍这方面的内容。还记得在前文介绍ngx_cycle_t结构时,它具有一个listening属性,是一个数组,存储所有监听socket,下面就来看看这些信息是什么时候添加的、以及如何初始化的。 1. 重要的数据

架构师专栏 1万+

Nginx源码分析 - 主流程篇 - Nginx的启动流程(09)

目录 一、Nginx的启动过程 二、重要流程分析 1. ngx_get_options 解析外部参数 2. init_cycle 初始化全局变量 3. 变量保存方法ngx_save_argv和ngx_process_options 4. 给模块打标ngx_preinit_modules 5. 创建PID文件ngx_create_pidfile 前几篇主要介绍了Nginx比较常用的一些基础数据结构,例如pool,buf,array,list等。通过对Nginx基础数据结构的理解,能更好的帮助我

alpha_love的博客 497

yolov8的Detect层详解(输出维度改动)

对于部署端需要拿到onnx的output进行后处理,但是基于anchor_free的yolov8输出是1*(4+cls)*8400,不是我们常见的ncwh格式,所以对Detect层进行了学习,分享一下体会。

yjcccccc的博客 2万+

nginx源码分析—模块及其初始化(二)

ontent 0. 序 1. nginx有哪些模块? 2. nginx如何描述这些模块? 2.1 模块数据结构 2.1.1 ngx_module_t结构 2.1.2 ngx_command_t结构 2.2 模块类图 3. nginx如何组织这些模块? 3.1 全局数组ngx_modules 3.2 模块组织结构图 4. nginx的

BOKE 787

C++利用spdlog生成日志

C++利用spdlog生成日志下载编译spdlog配置并使用 环境:Win10-x64+VS2015 spdlog是一个快速的C++日志库,能够兼容C++11,具备快速、无需依赖第三方库、支持多线程、多平台的优良特性! 下载编译spdlog 1.第一步,下载spdlog 下载spdlog,下载地址:https://github.com/gabime/spdlog#header-only-version,从Github上下载zip压缩包。下载完成后会获取一个名为spdlog-1.x.zip的压缩包 2.第二步

weixin_42975610的博客 3221

基于PCIE8311+FPGA+TMS320F28335 PCIE应用项目开发板PROTEL99设计硬件(原理图+PCB)文件

基于PCIE8311+FPGA+TMS320F28335 PCIE X1应用项目开发板PROTEL99设计硬件(原理图+PCB)文件,Protel 99se 设计的项目工程文件,包括原理图及PCB印制板图,可用Protel或 Altium Designer(AD)软件打开或修改,都已经制板在实际项目中使用,可作为你产品设计的参考。

GJZGRB的博客 577

Nginx 源码分析

nginx.caspect:constants:nginx.hfilter:fixture:lang:runtime:tools:

zjw12221的专栏 1136

MCP协议桥接AdsPower:AI原生时代的浏览器自动化新范式

浏览器自动化是Web开发和数据采集领域的核心技术,其核心原理是通过程序控制浏览器内核执行页面导航、元素交互和数据提取等操作。传统方案如Selenium和Playwright虽功能强大,但在管理多实例、指纹隔离等复杂场景下,配置和维护成本较高。随着AI原生应用的发展,Model Context Protocol(MCP)作为一种标准化协议,为AI助手与外部工具间的通信提供了统一接口,显著提升了自动化流程的构建效率和自然语言交互能力。通过MCP Server桥接AdsPower指纹浏览器的本地API,开发者可将

weixin_30552811的博客 296

Nginx源码分析(25篇),【设计思想解读开源框架】

加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

2401_84265751的博客 809

别再乱改grub.cfg了!手把手教你用GRUB2自定义多系统启动菜单(含UEFI/BIOS双模式配置)

本文详细介绍了如何安全、高效地使用GRUB2自定义多系统启动菜单,涵盖UEFI/BIOS双模式配置。通过避免直接编辑grub.cfg文件,采用正确的配置方法和命令,帮助用户打造稳定且美观的启动环境,特别适合Linux多系统用户和开发者。

weixin_42553471的博客 590

Python异步协程爬虫优化:解决aiohttp中ServerDisconnectedError的高效策略

本文深入探讨了在Python异步协程爬虫中使用aiohttp时,如何高效解决常见的ServerDisconnectedError问题。核心策略包括全局复用ClientSession、精细配置TCPConnector连接池、实施请求重试与速率限制,从而构建稳定、高效的异步爬虫架构,有效避免服务器断开连接错误。

pz890123的博客 857
上一篇: Nginx源码分析 - 主流程篇 - 全局变量cycle初始化(11)
下一篇: Nginx源码分析 - 主流程篇 - 解析配置文件(13)
老码农zhuli
老码农zhuli 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄14年 2456粉丝 312原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值