【nginx】nginx常用的数据结构

【nginx】nginx常用的数据结构(持续更新)

1、ngx_chain_t:存放buffer的单向链表

typedef struct ngx_chain_s ngx_chain_t;
struct ngx_chain_s {
    ngx_buf_t *buf;
    ngx_chain_t *next;
 };

在收到http请求时,每次Nginx都是读到一部分的内容,就放到链表,然后输出去。

2、ngx_buf_t:表示一块内存。
四个关键的成员指针:
start:指向内存的开始;
end:指向内存的结束;
last:指向内容的结束;
pos:指向当前处理到的内容。
ngx_buf_t结构图
如果 last等于end,就说明这块内存已经用完了。如果pos等于last,说明内存已经处理完了。

3、ngx_connection_t:一个tcp连接的生命周期
每个worker进程都有一个独立的连接池,连接池的大小是worker_connections。这里的连接池里面保存的其实不是真实的连接,它只是一个 worker_connections 大小的一个ngx_connection_t 结构的数组。并且,nginx 会通过一个链表 free_connections 来保存所有的空闲 ngx_connection_t,每次获取一个连接时,就从空闲连接链表中获取一个,用完后,再放回空闲连接链表里面。

4、ngx_http_request_t:保存解析请求与输出响应相关的数据。
4.1、解析请求行
收到http请求时,先从读取请求行开始,在ngx_http_process_request_line函数。
nginx为提高效率,采用状态机来解析请求行,而且在进行method的比较时,没有直接使用字符串比较,而是将四个字符转换成一个整型,然后一次比较以减少 cpu 的指令数。
4.2、解析头域
在解析完请求行后, nginx会设置读事件的handler为ngx_http_process_request_headers,然后后续的请求就在ngx_http_process_request_headers中进行读取与解析。解析到的请求头会保存到ngx_http_request_t的域headers_in(ngx_http_headers_in_t结构体)中,headers_in里有一个链表结构,保存所有的请求头,然后把一些需要特别处理的重要头域单独保存在ngx_http_headers_in_t结构体里的一个成员中,ngx_table_elt_t是一个映射表的一个元素。当每解析到一个请求头后,就会先在这个hash表中查找,如果有找到,则调用相应的处理函数来处理这个请求头。比如:Host 头的处理函数ngx_http_process_host。
产生的响应头会放在ngx_http_request_t的headers_out中。
4.3、解析玩请求后,处理请求
ngx_http_core_run_phases函数开始处理。
nginx的各种阶段会对请求进行处理,最后会调用filter来过滤数据,对数据进行加工,如truncked传输、gzip 压缩等。这里的filter包括header filter与body filter,即对响应头或响应体进行处理。filter 是一个链表结构,分别有 header filter与body filter,先执行header filter中的所有filter,然后再执行body filter中的所有 filter。在 header filter 中的最后一个 filter,即 ngx_http_header_filter,这个filter将会遍历所有的响应头,最后需要输出的响应头在一个连续的内存,然后调用ngx_http_write_filter 进行输出。ngx_http_write_filter是body filter中的最后一个,所以 nginx从开始的body信息,在经过一系列的body filter之后,最后也会调用ngx_http_write_filter来进行输出。

5、ngx_str_t
字符串的操作。

6、ngx_pool_t
代码实现:ngx_palloc.c、ngx_palloc.h
使用场景举例:http请求到来时,解析同一个请求消息都是在内存池内,如同pjsip。
nginx内场池内部的实现关键,同样是关注【结构组织】、【大块、小块的创建】、【释放】。
pool的结构由若干个小块和若干个大块构成。代码如下:

struct ngx_pool_large_s {
    ngx_pool_large_t     *next;
    void                 *alloc;
};// 大块数据
typedef struct {
    u_char               *last; //指向未分配的地址起始
    u_char               *end;//指向块的末尾
    ngx_pool_t           *next;
    ngx_uint_t            failed;
} ngx_pool_data_t; //小块数据
struct ngx_pool_s {
    ngx_pool_data_t       d;
    size_t                max;
    ngx_pool_t           *current;
    ngx_chain_t          *chain;
    ngx_pool_large_t     *large;
    ngx_pool_cleanup_t   *cleanup;
    ngx_log_t            *log;
};

小块先分配一个4K的大小,然后里面存放ngx_pool_data_t就指向这里内的空间,然后大块的地址则是由小块结构体里的指针去引出一个链表。
分配小块的流程:
(1)先从ngx_pool_s的current指针取出当前用到的小块。
(2)然后看ngx_pool_data_t里的last指针和end指针,判断当前小块的剩余空间是否足够,如果足够,则直接分配;
(3)不足够则再创建一块4K的空间(空间大小为ngx_pool_s结构体),在ngx_palloc_block函数。
分配大块的流程:
(1)先按上层指定的size大小,malloc一块内存空间。
(2)然后从ngx_pool_s的large指针来取出当前最后一个large块的ngx_pool_large_s结构体所在的内存地址(在小块的4k块里)。
(3)先看ngx_pool_large_s的结点的alloc指针是否为空,为空则表示这个ngx_pool_large_s是个没有指向大块内存的结点(一般是之前创建的,然后遇到大块释放过),然后就直接使用。否则,就继续往下遍历链表,直到找到三次或者遇到next为NULL的。
(4)在小块中分配一块内存在存放ngx_pool_large_s结构体,然后指向大块空间,即:往large链表后加新结点(头插法)。
pool的结构图如下:(该图参考来源:https://zhuanlan.zhihu.com/p/481302777)
nginx内存池结构图
函数接口如下:

ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
void ngx_destroy_pool(ngx_pool_t *pool);
void ngx_reset_pool(ngx_pool_t *pool);

void *ngx_palloc(ngx_pool_t *pool, size_t size);
void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);

ngx_pfree是对内存池里一块空间的释放,只能用于大块,且效率比较低,因此非必要最好是不调用。

7、ngx_array_t
数组,位于src/core/ngx_array.c|h,可以实现动态扩容,每次扩容是两倍增长。

8、ngx_hash_t
哈希表结构
基于ngx_hash_t,构建了ngx_hash_wildcard_t、ngx_hash_combined_t、ngx_hash_keys_arrays_t

9、ngx_list_t
一个特殊的链表结构,每个结点是ngx_list_part_t结构体。
它跟我们常见的链表实现的list有什么不同呢?不同点就在于它的结点,它的结点不像我们常见的list的结点只能存放一个元素,ngx_list_t的节点实际上是一个固定大小的数组,每个数组是一个ngx_list_part_t结构体(一个块)。

typedef struct {
 ngx_list_part_t *last;
 ngx_list_part_t part;
 size_t size;
 ngx_uint_t nalloc;
 ngx_pool_t *pool;
} ngx_list_t;
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; // 指向下一个块
};

需要说明一下,last始终指向最后一个节点,最后一个节点之前的节点可用内存空间均为0。

10、ngx_queue_t:双向链表

struct ngx_queue_s {
 ngx_queue_t *prev;
 ngx_queue_t *next;
};

使用时需要上层自己先添加哨兵结点。

11、ngx_command_t:一个配置项

struct ngx_command_s {
 ngx_str_t name;
 ngx_uint_t type;
 char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 ngx_uint_t conf;
 ngx_uint_t offset;
 void *post;
};

set指针:在nginx解析配置的时候,如果遇到该配置项,则会把读到的值传递给这个函数去处理。这个函数由上层自己去针对不同的配置项去指定。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值