nginx源码解析之常用数据结构

不问归期 提交于 2020-01-11 15:56:07

  1、基础数据结构(src/core目录)

  1)ngx_list_t(ngx_list.h)

typedef struct ngx_list_part_s ngx_list_part_t;  // 描述链表的一个元素(数组)

struct ngx_list_part_s {
    void *elts;  // 数组的起始地址
    ngx_uint_t nelts;  // 数组当前已使用了多少容量
    ngx_list_part_t *next;  // 下一个链表元素ngx_list_part_t的地址
};

typedef struct {
    ngx_list_part_t *last;  // 指向链表的最后一个数组元素
    ngx_list_part_t part;  // 链表的首个数组元素
    size_t size;  // 存储的每个数据的字节数必须小于或等于size
    ngx_uint_t nalloc;  // 每个ngx_list_part_t数组的容量
    ngx_pool_t *pool;  // 链表中管理内存分配的内存池对象
} ngx_list_t;  // 描述整个链表

  相关接口:

  ngx_list_create():创建新的链表。

  ngx_list_init():初始化一个已有的链表。返回NGX_OK表示成功,返回NGX_ERROR表示失败。

  ngx_list_push():添加新的元素。正常情况下返回新分配的元素的首地址,若失败则返回NULL

  没有遍历链表的接口。

  2)ngx_buf_t(ngx_buf.h)

typedef void *ngx_buf_tag_t;
typedef struct ngx_buf_s ngx_buf_t;
struct ngx_buf_s {
    u_char *pos;  // 通常表示本次应该从哪个位置开始处理内存中的数据
    u_char *last;  // 通常表示有效的内容到此为止
    off_t file_pos;  // 处理文件时,其含义与处理内存时的pos相同
    off_t file_last;  // 处理文件时,其含义与处理内存时的last相同

    u_char *start;  // ngx_buf_t用于内存时,该字段指向这段内存的起始地址
    u_char *end;  // 与start对应,指向缓冲区内存的末尾
    ngx_buf_tag_t tag;  // 表示当前缓冲区的类型,如由哪个模块使用就指向该模块ngx_module_t变量的地址
    ngx_file_t *file;  // 引用的文件
    ngx_buf_t *shadow;  // 当前缓冲区的影子缓冲区,很少使用

    unsigned temporary:1;  // 临时内存标志,1表示数据在内存中且这段内存可修改
    unsigned memory:1;  // 1表示数据在内存中且这段内存不可以被修改
    unsigned mmap:1;  // 1表示这段内存是用mmap映射过来的,不可以被修改

    unsigned recycled:1;  // 1表示可回收
    unsigned in_file:1;  // 1表示这段缓冲区处理的是文件而不是内存
    unsigned flush:1;  // 1表示需要执行flush操作
    unsigned sync:1;  // 操作这块缓冲区时是否使用同步方式(可能阻塞nginx进程)。nginx中所有操作几乎都是异步的,这是它支持高并发的关键
    unsigned last_buf:1;  // 是否是最后一块缓冲区(如由ngx_chain_t串联起来时)
    unsigned last_in_chain:1;  // 是否是ngx_chain_t中的最后一块缓冲区

    unsigned last_shadow:1;  // 是否是最后一个影子缓冲区,与shadow字段配合使用
    unsigned temp_file:1;  // 当前缓冲区是否属于临时文件

    /* STUB */ int num;
};

  3)ngx_table_elt_t(ngx_hash.h)

typedef struct {
    ngx_uint_t hash;  // 可用于在ngx_hash_t中更快地找到相同key的ngx_table_elt_t
    ngx_str_t key;  // 应用:如存储HTTP头部名称
    ngx_str_t value;  // 应用:如存储HTTP头部名称对应的值
    u_char *lowcase_key;  // 指向全小写的key。应用:如忽略HTTP头部名称大小写
} ngx_table_elt_t;

  4)ngx_command_t(ngx_conf_file.h):定义了一个配置项。

typedef struct ngx_command_s ngx_command_t;
struct ngx_command_s {
    ngx_str_t name;  // 配置项名称
    ngx_uint_t type;  // 配置项类型,决定了配置项可以在哪些块中出现,以及可以携带的参数类型和个数等
    /* 出现name配置项后,调用该函数处理配置项的参数。set既可以自定义,也可以使用nginx预设的14个函数,       如ngx_conf_set_flag_slot()(配置项参数为on/off且使用ngx_flag_t变量保存配置项参数时)、ngx_conf_set_str_slot() */
    char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);    /* 对于HTTP模块,conf是必须设置的。可取值:       NGX_HTTP_MAIN_CONF_OFFSET:使用ngx_http_module_t结构的create_main_conf()产生的结构体来存储解析出的配置项参数。       NGX_HTTP_SRV_CONF_OFFSET:使用create_srv_conf()产生的结构体来存储解析出的配置项参数。       NGX_HTTP_LOC_CONF_OFFSET:使用create_loc_conf()产生的结构体来存储解析出的配置项参数。 */
    ngx_uint_t conf;  // 在配置文件中的偏移量    /* 当前配置项在整个存储配置项的结构体中的偏移位置(字节)。可借助stddef.h的offsetof宏求得 */
    ngx_uint_t offset;  // 需要与conf配合使用    /* 若自定义了配置项的回调函数,则post的用途完全由用户定义;若使用的是nginx预设的配置项解析函数,则需要根据这些函数决定post的使用方式:       如对于ngx_conf_set_flag_slot(),可设置为NULL,或指向ngx_conf_post_t结构。该结构包含一个函数指针,表示在解析完配置项后回调这个函数。       如对于ngx_conf_set_enum_slot(),应把post指向ngx_conf_enum_t数组(规定了当前配置项参数的取值范围)。       如对于ngx_conf_set_bufs_slot(),post无任何用处。 */
    void *post;
};

  5)ngx_module_t(ngx_module.h)

typedef struct ngx_module_s ngx_module_t;
struct ngx_module_s {
/* ctx_index/index/spare0~spare3/version可以使用该宏来赋值 */
#define NGX_MODULE_V1 0, 0, 0, 0, 0, 0, 1

    /* 当前模块在某一类(type)模块中的序号。通常由管理这类模块的一个核心模块设置(如HTTP模块由ngx_http_module设置)。       nginx的模块化设计非常依赖于各个模块的顺序,它们既用于表达优先级,也用于表明每个模块的位置,借以帮助nginx框架快速获得某个模块的数据 */
    ngx_uint_t ctx_index;
    ngx_uint_t index;  // 当前模块在ngx_modules数组中的序号
    ngx_uint_t spare0;  // spare0~spare3为保留字段
    // ...
    ngx_uint_t version;  // 模块版本,便于将来的扩展。目前只有一种,默认为1
    void *ctx;  // 指向一类模块的上下文结构体(公共接口),如HTTP模块的ctx需要指向ngx_http_module_t结构体
    ngx_command_t *commands;  // 处理nginx.conf中的配置项
    ngx_uint_t type;  // 模块类型,如HTTP模块类型为NGX_HTTP_MODULE
    ngx_int_t (*init_master)(ngx_log_t *log);  // 目前不会被调用,可设置为NULL
    ngx_int_t (*init_module)(ngx_cycle_t *cycle);  // 在初始化所有模块时被调用。在master/worker模式下,这个阶段将在启动worker子进程前完成
    /* 在正常服务前被调用。在master/worker模式下,多个worker子进程已经产生,在每个worker进程的初始化过程会调用所有模块的init_process() */
    ngx_int_t (*init_process)(ngx_cycle_t *cycle);
    ngx_int_t (*init_thread)(ngx_cycle_t *cycle);  // nginx暂不支持多线程,故设为NULL
    void (*exit_thread)(ngx_cycle_t *cycle);  // 同上
    void (*exit_process)(ngx_cycle_t *cycle);  // 在服务停止前调用。在master/worker模式下,worker进程会在退出前调用它
    void (*exit_master)(ngx_cycle_t *cycle);  // 在master进程退出前被调用
    uintptr_t spare_hook0;  // spare_hook0~spare_hook7为保留字段,可以使用NGX_MODULE_V1_PADDING宏来赋值
    // ...
};

 

  2、HTTP相关数据结构(src/http目录)

  1)ngx_http_headers_in_t(ngx_http_request.h)

typedef struct {
    ngx_list_t headers;  // 包含所有解析过的HTTP头部
    /* 以下ngx_table_elt_t *类型成员实际上都指向了headers中的相应成员 */
    ngx_table_elt_t *host;
    ngx_table_elt_t *connection;
    // ... ...
    ngx_array_t cookies;
    ngx_str_t server;  // server名称
    // ...
    /* HTTP连接类型(0/NGX_HTTP_CONNECTION_CLOSE/NGX_HTTP_CONNECTION_KEEP_ALIVE) */
    unsigned connection_type:2;
    /* 以下标志位用于判断浏览器类型(根据浏览器传来的useragent头部设置) */
    unsigned chunked:1;
    unsigned msie:1;
    unsigned msie6:1;
    unsigned opera:1;
    unsigned gecko:1;
    unsigned chrome:1;
    unsigned safari:1;
    unsigned konqueror:1;
} ngx_http_headers_in_t;

  2)ngx_http_headers_out_t(ngx_http_request.h)

typedef struct {
    ngx_list_t headers;  // 待发送的HTTP(响应)头部链表

    ngx_uint_t status;  // 响应中的状态值,如200
    ngx_str_t status_line;  // 响应的状态行,如"HTTP/1.1 201 CREATED"

    // 以下成员设置后,ngx_http_header_filter_module过滤模块可以把它们加到待发送的网络包中
    ngx_table_elt_t *server;
    ngx_table_elt_t *date;
    ngx_table_elt_t *content_length;
    ngx_table_elt_t *content_encoding;
    ngx_table_elt_t *location;
    ngx_table_elt_t *refresh;
    ngx_table_elt_t *last_modified;
    ngx_table_elt_t *content_range;
    ngx_table_elt_t *accept_ranges;
    ngx_table_elt_t *www_authenticate;
    ngx_table_elt_t *expires;
    ngx_table_elt_t *etag;

    ngx_str_t *override_charset;

    // ngx_http_set_content_type()函数可根据URI中的文件扩展名并对应着mine.type来设置Content-Type
    size_t content_type_len;
    ngx_str_t content_type;
    ngx_str_t charset;
    u_char *content_type_lowcase;
    ngx_uint_t content_type_hash;

    ngx_array_t cache_control;

    off_t content_length_n;  // 设置该成员后,不需要再到content_length中设置响应长度
    off_t content_offset;
    time_t date_time;
    time_t last_modified_time;
} ngx_http_headers_out_t;

  3)ngx_http_request_t(ngx_http_request.h):包含请求的所有信息(方法、URI、协议版本号和头部等)。

typedef struct ngx_http_request_s ngx_http_request_t;
struct ngx_http_request_s {
    // ...
    ngx_pool_t *pool;  // 指向该请求的内存池管理对象
    ngx_buf_t *header_in;  // 指向nginx收到的未经解析的HTTP头部(就是接收HTTP头部的缓冲区)
    ngx_http_headers_in_t headers_in;  // 存储已经解析过的HTTP(请求)头部
    ngx_http_headers_out_t headers_out;  // 存储响应中的HTTP头部
    // ...
    ngx_uint_t method;  // 如NGX_HTTP_GET、NGX_HTTP_HEAD
    ngx_uint_t http_version;

    ngx_str_t request_line;
    ngx_str_t uri;
    ngx_str_t args;  // 用户请求中的URL参数
    ngx_str_t exten;  // 用户请求的文件扩展名
    ngx_str_t unparsed_uri;  // 未进行URL解码的原始请求,如"/a%20b"

    ngx_str_t method_name;  // 用户请求中的方法名字符串
    ngx_str_t http_protocol;
    // ...
    u_char *uri_start;
    u_char *uri_end;  // 指向URI结束后的下一个地址
    u_char *uri_ext;
    u_char *args_start;
    u_char *request_start;  // nginx对内存的控制相当严格。为了避免不必要的内存开销,许多需要用到的成员都不是重新分配内存后存储的,
                            // 而是直接指向用户请求中的相应地址,如method_name.data和request_start这两个指针实际上指向的都是同一个地址
    u_char *request_end;
    u_char *method_end;  // 结合request_start与method_end(指向方法名的最后一个字符)可取得方法名
    u_char *schema_start;
    u_char *schema_end;
    // ...
};

  4)ngx_http_module_t(ngx_http_config.h)

typedef struct {
    ngx_int_t (*preconfiguration)(ngx_conf_t *cf);  // 解析配置文件前调用
    ngx_int_t (*postconfiguration)(ngx_conf_t *cf);  // 完成配置文件的解析后调用

    /* 当需要创建数据结构用于存储main级别(直属于http{}块的配置项)的全局配置项时,可以借助该回调函数 */
    void *(*create_main_conf)(ngx_conf_t *cf);
    char *(*init_main_conf)(ngx_conf_t *cf, void *conf);  // 常用于初始化main级别配置项

    /* 当需要创建数据结构用于存储srv级别(直属于虚拟主机server{}块)的配置项时,可以借助该回调函数 */
    void *(*create_srv_conf)(ngx_conf_t *cf);
    char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);  // 主要用于合并main级别和srv级别下的同名配置项

    /* 当需要创建数据结构用于存储loc级别(直属于location{}块)的配置项时,可以借助该回调函数 */
    void *(*create_loc_conf)(ngx_conf_t *cf);    /* 主要用于合并srv级别和loc级别下的同名配置项(合并方式可自定义,也可使用nginx预设的合并宏,如ngx_conf_merge_str_value)。参数:       cf:提供一些基本数据结构(如内存池、日志);prev:指向解析父配置块时生成的结构体;conf:指向保存子配置块的结构体 */
    char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;

  5)ngx_http_conf_ctx_t(ngx_http_config.h)

/* 当nginx检测到http{}时,HTTP配置模型就启动了,这时会首先建立1个ngx_http_conf_ctx_t结构   HTTP框架会为所有的HTTP模块建立3个数组,分别存放所有HTTP模块的create_main_conf()、create_srv_conf()和create_loc_conf()返回的指针   main_conf、srv_conf和loc_conf分别指向这3个数组
*/
typedef struct {
    void **main_conf;
    void **srv_conf;
    void **loc_conf;
} ngx_http_conf_ctx_t;

 

 

  参考资料:

  《深入理解Nginx:模块开发与架构解析》

 

 

不断学习中。。。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!