资讯专栏INFORMATION COLUMN

Nginx 源码分析:从模块到配置(上)

gotham / 2955人阅读

摘要:结构体数组,用来表示该模块可以在配置文件中配置的项目,及其操作指令。

源文件路径

</>复制代码

  1. srccore
  2. gx_conf_file.h
  3. srccore
  4. gx_conf_file.c
主要内容

本篇的主要目的在于分析Nginx的配置功能。由于Nginx的配置基本就是对模块的配置,因此,在讨论配置功能之前,需要先分析Nginx的模块功能。

对于模块功能,这里的重点不在于某个模块的细节,而在于分析Nginx的模块架构是如何设计的。

类似地,因为对于Nginx的配置文件,没有资料比官方文档上说得更明白,所以,这里的重点并不是对配置文件,而是分析Nginx的配置功能是如何设计的。

好了,开始吧。

模块功能

Nginx的基础架构是高度模块化的。可以从多个方面来观察Nginx的模块化。

官方文档首页的整个下半页是Nginx自身所有模块的列表。从这里可以看到Nginx的模块轮廓。

如果从源码安装Nginx,那么首先执行的configure脚本的配置参数里,很大一部分是用来管理是否安装某个模块的。当然,Nginx运行必备的模块是必须安装的,因此,并不会出现在configure的配置选项里。

Nginx本身由多个基本的模块构成,其中,核心的部分是一个叫ngx_core_module的模块。当然,对于一个web服务器,仅仅有一个核心是不够的,还需要大量的“辅助模块”。这有点像Linux的设计,一堆外围设施作为模块与Linux内核构成整个Linux系统。

很自然的会想到,Nginx的这种模块化设计是支持第三方插件的。这种设计也大大增加了Nginx的弹性和能力。

模块的组织和管理

既然Nginx是由许多模块构成的,那么,如何组织和管理这些模块是首先要关注的问题。在Nginx中,使用全局数组ngx_modules保存和管理所有Nginx的模块

如何做到的呢?

首先,Nginx的众多模块被分成两类:必须安装的模块可以安装的模块

必须安装的模块是保证Nginx正常功能的模块,没得选择,这些模块会出现在ngx_modules里。比如ngx_core_module

可以安装的模块通过configure的配置和系统环境,被有选择的安装,这些模块里,被选择安装的模块会出现在ngx_modules数组中。

configure执行结束后,会生成一个objs gx_modules.c的源代码文件:
文件内容随configure配置不同而不同,例如:

</>复制代码

  1. #include
  2. #include
  3. extern ngx_module_t ngx_core_module;
  4. extern ngx_module_t ngx_errlog_module;
  5. extern ngx_module_t ngx_conf_module;
  6. extern ngx_module_t ngx_events_module;
  7. extern ngx_module_t ngx_event_core_module;
  8. extern ngx_module_t ngx_epoll_module;
  9. extern ngx_module_t ngx_regex_module;
  10. extern ngx_module_t ngx_http_module;
  11. extern ngx_module_t ngx_http_core_module;
  12. extern ngx_module_t ngx_http_log_module;
  13. extern ngx_module_t ngx_http_upstream_module;
  14. extern ngx_module_t ngx_http_static_module;
  15. extern ngx_module_t ngx_http_autoindex_module;
  16. extern ngx_module_t ngx_http_index_module;
  17. ...
  18. ngx_module_t *ngx_modules[] = {
  19. &ngx_core_module,
  20. &ngx_errlog_module,
  21. &ngx_conf_module,
  22. &ngx_events_module,
  23. &ngx_event_core_module,
  24. &ngx_epoll_module,
  25. &ngx_regex_module,
  26. &ngx_http_module,
  27. &ngx_http_core_module,
  28. &ngx_http_log_module,
  29. &ngx_http_upstream_module,
  30. &ngx_http_static_module,
  31. &ngx_http_autoindex_module,
  32. &ngx_http_index_module,
  33. ...
  34. NULL
  35. };

由于数组存放的是同一类型的数据。因此,必须有一个模块基类来抽象的定义所有模块。这个抽象基类就是ngx_module_t

因此,可以看到ngx_modules数组里存放的是ngx_module_t *类型指针,并且,最后一个一定是空指针NULL

将最后一个元素置为空指针NULL,是为了遍历数组的时候,标记数组结束,这样不必在使用时记录数组大小。

例如:

</>复制代码

  1. for (i = 0; ngx_modules[i]; i++) {
  2. ...
  3. }

使用extern ngx_module_t ngx_core_module;使编译器在编译的时候不去处理ngx_core_module,在链接阶段,再将ngx_core_module链接到正确的地址中。

类似地,在源文件core gx_conf_file.h的末尾声明外部ngx_modules[]

</>复制代码

  1. extern ngx_module_t *ngx_modules[];

由于core gx_conf_file.h会被当作公共头文件被包含到其他文件中,因此,ngx_modules是一个全局变量

======分割线=======
在编译阶段,objs gx_modules.c文件中的那一堆extern变量及ngx_modules变量都会被初始化。原因在于,这些变量都属于全局变量,将会被静态初始化
======分割线=======

这样,有了全局数组ngx_modules,就可以获取所有模块的指针,从而操作所有模块。对所有模块的管理和操作也由此展开。

所有模块的基类 ngx_module_t

所有模块都是ngx_module_t类型的变量,其定义如下:

</>复制代码

  1. struct ngx_module_s {
  2. ngx_uint_t ctx_index;
  3. ngx_uint_t index;
  4. ngx_uint_t spare0;
  5. ngx_uint_t spare1;
  6. ngx_uint_t spare2;
  7. ngx_uint_t spare3;
  8. ngx_uint_t version;
  9. void *ctx;
  10. ngx_command_t *commands;
  11. ngx_uint_t type;
  12. ngx_int_t (*init_master)(ngx_log_t *log);
  13. ngx_int_t (*init_module)(ngx_cycle_t *cycle);
  14. ngx_int_t (*init_process)(ngx_cycle_t *cycle);
  15. ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
  16. void (*exit_thread)(ngx_cycle_t *cycle);
  17. void (*exit_process)(ngx_cycle_t *cycle);
  18. void (*exit_master)(ngx_cycle_t *cycle);
  19. uintptr_t spare_hook0;
  20. uintptr_t spare_hook1;
  21. uintptr_t spare_hook2;
  22. uintptr_t spare_hook3;
  23. uintptr_t spare_hook4;
  24. uintptr_t spare_hook5;
  25. uintptr_t spare_hook6;
  26. uintptr_t spare_hook7;
  27. };

各成员变量含义如下

1. type

type变量是指模块的类型。Nginx中模块的类型定义如下:

</>复制代码

  1. #define NGX_CORE_MODULE 0x45524F43 /* "CORE" */
  2. #define NGX_CONF_MODULE 0x464E4F43 /* "CONF" */
  3. #define NGX_EVENT_MODULE 0x544E5645 /* "EVNT" */
  4. #define NGX_HTTP_MODULE 0x50545448 /* "HTTP" */
  5. #define NGX_MAIL_MODULE 0x4C49414D /* "MAIL" */

也就是说,Nginx共有5中模块类型CORFCONFEVNTHTTPMAIL

2. index

index变量是指,该模块在ngx_modules数组中的次序,或者说下标。该变量在Nginx执行初始化的时候被初始化,初始化代码位于core ginx.cmain函数中:

</>复制代码

  1. ngx_max_module = 0;
  2. for (i = 0; ngx_modules[i]; i++) {
  3. ngx_modules[i]->index = ngx_max_module++;
  4. }

3. ctx_index

ctx_index是指,在ngx_modules数组中,该模块在相同类型的模块中的次序。

例如,ngx_modules数组中有多个类型为NGX_EVENT_MODULE的模块,那么其中一个模块的ctx_index初始化如下:

</>复制代码

  1. // 源码文件event
  2. gx_event.c
  3. ngx_event_max_module = 0;
  4. for (i = 0; ngx_modules[i]; i++) {
  5. if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
  6. continue;
  7. }
  8. ngx_modules[i]->ctx_index = ngx_event_max_module++;
  9. }

4. ctx

ctxvoid *指针型变量,这是指向与模块相关的上下文

这里先解释一下什么叫与模块相关的上下文

上下文这个概念一般可以理解为运行环境,那么与模块相关的上下文就是指模块运行环境。换句话说,就是一些参数的配置信息。因为配置一般意味着设置,而设置的对象就是模块的运行环境。比如,ngx_core_modules运行时的usergroup,就属于运行环境,同时,也属于配置项。

由于模块的参数一般比较多,正确设置参数的配置需要很多操作。所以,上下文其实也意味着一组操作,这组操作的目的在于根据配置,正确设置模块的参数信息。同时,这也意味着,不同的模块,这组操作是不同的,因此,这里需要将ctx设置为void *指针,来进行泛型的操作。

ngx_core_module为例,其ctx指向的上下文是这个样子的:

</>复制代码

  1. static ngx_core_module_t ngx_core_module_ctx = {
  2. ngx_string("core"),
  3. ngx_core_module_create_conf,
  4. ngx_core_module_init_conf
  5. };

其中ngx_core_module_t的定义如下:

</>复制代码

  1. typedef struct {
  2. ngx_str_t name;
  3. void *(*create_conf)(ngx_cycle_t *cycle);
  4. char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
  5. } ngx_core_module_t;

而这组操作中的ngx_core_module_init_conf函数中,初始化了一个ngx_core_conf_t的结构体。

因此,可知,void *ctx指向了一组操作,这组操作的目的在于初始化一个与模块配置信息相关的结构体
这里涉及到了三个结构体:

ngx_module_t ngx_core_module:用来表示模块本身,保存在ngx_modules数组中;

ngx_core_conf_t core_conf:用来保存对该模块的配置信息;

ngx_core_module_t ngx_core_module_ctx:用来初始化ngx_core_conf_t中的成员变量;

6. commands

commands是ngx_command_t数组,表示一组配置文件中的可配项(指令)。

例如,在配置文件nginx.confworker_processes 1;对应commands数组中的一项,该项的定义如下,类型为ngx_command_t

</>复制代码

  1. { ngx_string("worker_processes"),
  2. NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
  3. ngx_set_worker_processes,
  4. 0,
  5. 0,
  6. NULL }

其中ngx_command_t定义如下:

</>复制代码

  1. struct ngx_command_s {
  2. ngx_str_t name;
  3. ngx_uint_t type;
  4. char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
  5. ngx_uint_t conf;
  6. ngx_uint_t offset;
  7. void *post;
  8. };

所以,commands的含义在于该模块的配置项在配置文件中的指令

ngx_module_t 总结

到这里,一个完整的模块所需的各个部分逐渐显露出来了:

ngx_module_t结构体,用来表示模块的定义,被放入ngx_modules数组中。

ngx__conf_t结构体,用来表示模块的配置项,这个结构体随模块的不同而不同,通过上下文操作及commands中定义的指令,由配置文件确定其成员值。

ngx__module_t结构体,表示一组用来初始化ngx__conf_t结构体的操作。在Nginx初始化时被调用,初始化ngx__conf_t结构体。

ngx_command_t结构体数组,用来表示该模块可以在配置文件中配置的项目,及其操作指令。在Nginx初始化解析配置文件后,调用ngx_command_t中的函数set初始化ngx__conf_t结构体。

总结

Nginx中,一个模块运行所需的各种配置参数被封装成了ngx__conf_t结构体。

这个结构体内成员变量的初始化被分成了两部分:

模块相关的上下文ngx__module_t:即定义了一组初始化操作,用来将该结构体内的各成员初始化。

配置文件相关指令ngx_command_t:即规定了配置文件中可以使用的指令及其对应的操作函数。

而模块本身被封装成了ngx_module_t类型结构体,被用来供Nginx统一管理和使用。

模块和配置的联系非常紧密,因此,这里仅仅先大略的分析一下模块功能的整体框架,下一篇中,将在本篇的基础上分析Nginx的配置功能。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/39155.html

相关文章

  • Nginx 源码分析模块配置(下)

    摘要:每个模块由以下几部分构成结构体代表模块本身,其指针被放入数组中。结构体用来表示模块的配置内容,其中部分成员可以通过配置文件进行配置。调用该中的函数,该函数最终初始化模块对应的结构体,完成配置。因此,分析源码中的配置指令,就是分析结构体。 本篇的上篇 Nginx 源码分析:从模块到配置(上),建议阅读本篇前先阅读上篇。 关于模块 Nginx的架构高度模块化。每个模块各司其职,组合在一...

    Chao 评论0 收藏0
  • Nginx源码分析Nginx配置文件解析(一)

    摘要:本文将从源码从此深入分析配置文件的解析,配置存储,与配置查找。在学习配置文件的解析过程之前,需要先了解一下模块与指令的一些基本知识。 运营研发团队 李乐 配置文件是nginx的基础,对于学习nginx源码甚至开发nginx模块的同学来说更是必须深究。本文将从源码从此深入分析nginx配置文件的解析,配置存储,与配置查找。 看本文之前读者可以先思考两个问题: 1.nginx源码中随处可以...

    JasonZhang 评论0 收藏0
  • Nginx源码分析Nginx的listen处理流程分析

    摘要:四监听套接字的使用假设此处我们使用作为事件处理模块在增加事件时用户可以使用中的字段当事件发生时该字段也会带回。在创建监听套接字时将结构分为级监听套接字地址各级都是一对多的关系。 施洪宝 一. 基础 nginx源码采用1.15.5 后续部分仅讨论http中的listen配置解析以及优化流程 1.1 概述 假设nginx http模块的配置如下 http{ server { ...

    yuanzhanghu 评论0 收藏0
  • Nginx 源码分析:ngx_hash_t(

    摘要:现在使用的各种哈希函数基本上只能保证较小概率出现两个不同的其相同的情况。而出现两个值对应的相同的情况,称为哈希冲突。中的哈希表需要指出的是,中自造的哈希表属于内部使用的数据结构,因此,并不是一个通用的哈希表。 源文件路径 版本:1.8.0 csrccoreNgx_hash.h srccoreNgx_hash.c 关于hash表 Nginx实现的hash表和常见的hash表大体...

    waruqi 评论0 收藏0

发表评论

0条评论

gotham

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<