nginx源码解析之常用数据结构
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:模块开发与架构解析》
不断学习中。。。