这是一篇草稿型札记,主要用来收集零散和不完整的记录,用于进行渐进式归纳。之前尝试过从main函数开始按顺序学习总结,但是发现nginx源码非常庞大,各种功能在不理解的前提下看起来互相嵌套耦合,导致容易迷失且理解有偏差,所有先写点零零散散的分析,不断的修正总结吧。

环境搭建备注

Nginx版本下载地址: nginx-1.14.2

建立了GitHub源码学习地址,主要是一些注释记录:nginx-1.14.2-annotated

编译环境是一台云服务器:Ubuntu Server 18.04.1 LTS 64位 1核 2GB 1Mbps,编译工具为: gcc version 7.4.0

使用了两种编辑器:

  1. 通过xshell/iTerm2远程 + vim 8.0.1453(部分系统自带7版本,建议更新到vim 8版本才能使用某些插件), vim插件基本配置参考文章: vim插件配置
  2. 另一些时候使用vscode+remote插件。

快捷键备注

vim快捷键

  • v框选一块代码,y复制
  • hjkl左下右上
  • $到本行行尾,0到本行开头

vscode快捷键

  • alt+左右箭头函数前后跳转

规划

  1. nginx编译,配置文件解析
  2. 内存池相关
  3. 数据结构相关

一些临时记录🔰

configure脚本

几个关键步骤:

  • . auto/options模块定义了后续工作(包括项目代码里)需要用到的变量,根据调用./configure --xx时携带的参数以及默认值设置这些变量。
  • . auto/modules模块用来组建ngx_module_t ngx_modules[]数组,这个结构数组非常重要,具体作用后面展开来说。

产生结果

configure执行成功时会生成objs目录,并在该目录下生成以下目录和文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
├── autoconf.err        //保存configure执行过程中产生的结果
├── Makefile            //用于编译nginx工程及install参数安装nginx程序
├── ngx_auto_config.h   //会被src/core/ngx_config.h和
├── ngx_auto_headers.h  //src/os/unix/ngx_linux_config.h文件引用
├── ngx_modules.c       //这是一个关键文件,下边展开讲
└── src                 //src目录用于存放编译时产生的目标文件
    ├── core
    ├── event
    │   └── modules
    //....

nginx.conf配置文件

nginx.conf通用配置结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
...         # 全局块
events {    # events块
    ...
}

http {      # http块
    ...     # http全局块
    server {    # server块
        ...     # server全局块
        location [PATTERN] {    # location块
            ...
        }
        location [PATTERN] {
            ...
        }
    }
    server {
        ...
    }
    ...     # http全局块
}
...
  1. 全局块:配置影响nginx全局的指令。一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等。
  2. events块:配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。
  3. http块:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等。
  4. server块:配置虚拟主机的相关参数,一个http中可以有多个server。
  5. location块:配置请求的路由,以及各种页面的处理情况。

ngx_module_t结构

从上面章节可知,configure脚本执行后,产生了ngx_module[]数组:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
ngx_module_t *ngx_modules[] = {
      &ngx_core_module,
      &ngx_errlog_module,
      &ngx_conf_module,
      &ngx_events_module,
      &ngx_event_core_module,
      &ngx_kqueue_module,
      &ngx_stream_module,
      &ngx_stream_core_module,
      &ngx_stream_write_filter_module,
      &ngx_stream_return_module,
      // ...
      NULL
};

该数组的类型为ngx_module_t类型,该类型定义在ngx_module.c文件中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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;
};

/* 该类型还定义了两个宏,来初始化一些通用成员 */
#define NGX_MODULE_V1                                                         \
    NGX_MODULE_UNSET_INDEX, NGX_MODULE_UNSET_INDEX,                           \
    NULL, 0, 0, nginx_version, NGX_MODULE_SIGNATURE

#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0

以下是三个具体的ngx_module_t类型声明,ngx_events_modulengx_core_modulengx_http_log_module结构体:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
ngx_module_t  ngx_events_module = {
    NGX_MODULE_V1,
    &ngx_events_module_ctx,                /* module context */
    ngx_events_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
};

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
};

ngx_module_t  ngx_http_log_module = {
    NGX_MODULE_V1,
    &ngx_http_log_module_ctx,              /* module context */
    ngx_http_log_commands,                 /* module directives */
    NGX_HTTP_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
};

由上可知NGX_MODULE_V1NGX_MODULE_V1_PADDING都是用来快速填充的宏,多是空值填充。最核心的是void *ctxngx_command_t commandsnginx_unit_t type三个结构。

type就是模块的类型,每种类型有不同的ctxcommandsngx_events_modulengx_core_moduletypeNGX_CORE_MODULE类型,对应的ctx都是ngx_core_module_t类型的,ngx_http_log_module类型的typeNGX_HTTP_MODULE类型,对应的ctxngx_http_module_t类型的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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;

static ngx_core_module_t  ngx_events_module_ctx = {
    ngx_string("events"),
    NULL,
    ngx_event_init_conf
};

static ngx_core_module_t  ngx_core_module_ctx = {
    ngx_string("core"),
    ngx_core_module_create_conf,
    ngx_core_module_init_conf
};

static ngx_http_module_t  ngx_http_log_module_ctx = {
    NULL,                                  /* preconfiguration */
    ngx_http_log_init,                     /* postconfiguration */

    ngx_http_log_create_main_conf,         /* create main configuration */
    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */
    NULL,                                  /* merge server configuration */

    ngx_http_log_create_loc_conf,          /* create location configuration */
    ngx_http_log_merge_loc_conf            /* merge location configuration */
};

typedef struct {
    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);
    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);

    void       *(*create_main_conf)(ngx_conf_t *cf);
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);

    void       *(*create_srv_conf)(ngx_conf_t *cf);
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);

    void       *(*create_loc_conf)(ngx_conf_t *cf);
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
struct ngx_command_s {
    ngx_str_t             name;     /* 配置项名称 */
    /* 配置项类型,type将指定配置项可以出现的位置。例如,出现在server{ }或
     * location{ }中,以及它可以携带的参数个数。
     * type可以同时取多个值,各值之间用|符号连接,例如,type可以取值为
     * NGX_TTP_MAIN_CONF | NGX_HTTP_SRV_CONFI | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE。
     * */
    ngx_uint_t            type;
    /* 出现了name中指定的配置项后,讲调用set方法处理配置项的参数 */
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    /* crate分配内存的时候的偏移量 NGX_HTTP_LOC_CONF_OFFSET NGX_HTTP_SRV_CONF_OFFSET */
    ngx_uint_t            conf;
    /*  通常用于使用预设的解析方法解析配置项,这是配置模块的一个优秀设计。
     *  它需要与conf配合使用
     *  */
    ngx_uint_t            offset;
    /* 如果使用Nginx预设的配置项解析方法,就需要根据这些预设方法来决定post的使用方式。 */
    void                 *post;
};

static ngx_command_t  ngx_events_commands[] = {

    { ngx_string("events"),
      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
      ngx_events_block,
      0,
      0,
      NULL },

      ngx_null_command
};

static ngx_command_t  ngx_core_commands[] = {

    { ngx_string("daemon"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      0,
      offsetof(ngx_core_conf_t, daemon),
      NULL },

    { ngx_string("master_process"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      0,
      offsetof(ngx_core_conf_t, master),
      NULL },
    //.省略跟多项.......
    { ngx_string("env"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
      ngx_set_env,
      0,
      0,
      NULL },

    { ngx_string("load_module"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
      ngx_load_module,
      0,
      0,
      NULL },

      ngx_null_command
};


static ngx_command_t  ngx_http_log_commands[] = {

    { ngx_string("log_format"),
      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
      ngx_http_log_set_format,
      NGX_HTTP_MAIN_CONF_OFFSET,
      0,
      NULL },

    { ngx_string("access_log"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
                        |NGX_HTTP_LMT_CONF|NGX_CONF_1MORE,
      ngx_http_log_set_log,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },

    { ngx_string("open_log_file_cache"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
      ngx_http_log_open_file_cache,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },

      ngx_null_command
};

nginx配置文件解析

上一节中我们讲了ngx_module_t结构体,其中有三个关注点:ctx,commands_s,types。配置文件解析相关的types类型为NGX_CORE_MODULE,其对应的ctxcommands_s类型为:ngx_events_module_ctxngx_events_commands

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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
2
3
4
5
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;
1
2
3
4
5
static ngx_core_module_t  ngx_core_module_ctx = {
    ngx_string("core"),
    ngx_core_module_create_conf,
    ngx_core_module_init_conf
};

当nginx启动时,配置文件解析开始于main函数里的ngxinit_cycle函数。这一步的目的是调用核心函数模块的create_conf函数,为配置信息分配内存空间,并且对一些配置变量进行NGX_CONF_UNSET的初始化。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
for (i = 0; cycle->modules[i]; i++) {
    if (cycle->modules[i]->type != NGX_CORE_MODULE) {/* 只处理ngx_core_module模块 */
        continue;
    }

    module = cycle->modules[i]->ctx;

    if (module->create_conf) {
        rv = module->create_conf(cycle);    /* 为配置信息分配内存空间 */
        if (rv == NULL) {
            ngx_destroy_pool(pool);
            return NULL;
        }
        cycle->conf_ctx[cycle->modules[i]->index] = rv;
    }
}
1
2
3
4
5
if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
    environ = senv;
    ngx_destroy_cycle_pools(&conf);
    return NULL;
}

ngx_conf_parse函数开始真正的配置文件解析。在该函数内部,

1
2