nginx接收nginx服务器的http请求包头、包体,以及响应客户端的应答包头、包体都会存放到chain链表缓冲区中。
一、chain链表缓冲区数据结构
//ngx_chain_t链表节点
struct ngx_chain_s
{
ngx_buf_t *buf;
ngx_chain_t *next;
};
//缓冲区结构
struct ngx_buf_s
{
u_char *pos; //缓冲区有效数据开始位置
u_char *last; //缓冲区有效数据结束位置
off_t file_pos; //文件开始位置
off_t file_last; //文件结束位置
u_char *start; //整个缓冲区的首地址
u_char *end; //整个缓冲区的末地址
ngx_buf_tag_t tag; //由哪个模块使用就执行那个模块的ngx_module_t结构,例如ngx_http_proxy_module
ngx_file_t *file; //文件对象
ngx_buf_t *shadow; //影子缓冲区链表头结点,该链表下的所有节点的buf指向同一个空间
unsigned temporary:1; //临时内存标记,值为1表示这段数据在内存中可以修改,
//默认创建的都是可以修改的临时缓冲
unsigned memory:1; //值为1表示这段数据在内存中 且不可以修改
unsigned mmap:1; //表示这个buf是否通过调用mmap获取到的
unsigned recycled:1; //值为1时表示可以回收
unsigned in_file:1; //是否发送文件
unsigned flush:1;
unsigned sync:1;
unsigned last_buf:1; //表示是否为最后一个缓冲区
unsigned last_in_chain:1;
unsigned last_shadow:1; //是否为影子缓冲区链表的最后一个节点
unsigned temp_file:1;
};
其中start与pos之间的数据为nginx读取成功并解析完成。
pos与last之间的数据为nginx读取成功的数据,但没有解析
last与end之间的空间为buf的剩余空间,新接收的数据将保持在last指针指向的空间。
二、创建一个缓冲区buf调用ngx_create_temp_buf函数将在内存池上创建一个临时缓冲区
//创建一个ngx_buf_t结构,同时将开辟一个大小为size的缓冲区
//参数: size 缓冲区大小
ngx_buf_t * ngx_create_temp_buf(ngx_pool_t *pool, size_t size)
{
ngx_buf_t *b;
//创建一个缓冲区对象
b = ngx_calloc_buf(pool);
if (b == NULL)
{
return NULL;
}
//开辟缓冲区空间
b->start = ngx_palloc(pool, size);
if (b->start == NULL)
{
return NULL;
}
//更新缓冲区开始与结束位置
b->pos = b->start;
b->last = b->start;
b->end = b->last + size;
//临时缓冲区,内存可以被修改
b->temporary = 1;
return b;
}
三、创建一个缓冲区链表
ngx_create_temp_buf只是创建了单个缓冲区空间。如果要创建一片连续的缓冲区,就由链表统一管理,则需要使用ngx_create_chain_of_bufs函数。
//创建一个有bufs->num个缓冲区,每个缓冲区大小为bufs->size。并将这些缓冲区通过ngx_chain_t链表连接起来。
//返回值: ngx_chain_t链表头结点
ngx_chain_t * ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs)
{
u_char *p;
ngx_int_t i;
ngx_buf_t *b;
ngx_chain_t *chain, *cl, **ll;
//创建一个num个的缓冲区,每一个缓冲区大小为size.这片空间是连续的
p = ngx_palloc(pool, bufs->num * bufs->size);
if (p == NULL)
{
return NULL;
}
ll = &chain;
//将每一个缓冲区加入到ngx_chain_t链表
for (i = 0; i < bufs->num; i++)
{
//创建一个ngx_buf_t结构
b = ngx_calloc_buf(pool);
if (b == NULL)
{
return NULL;
}
//初始化每个buf缓冲区的起始位置
b->pos = p;
b->last = p;
b->temporary = 1;
b->start = p;
//此时p会指向下一个缓冲区
p += bufs->size;
b->end = p;
//创建一个ngx_chain_t节点
cl = ngx_alloc_chain_link(pool);
if (cl == NULL)
{
return NULL;
}
//将ngx_chain_t于buf关联起来
cl->buf = b;
*ll = cl;
//创建ngx_chain_t链表
ll = &cl->next;
}
*ll = NULL;
//返回ngx_chain_t表头节点
return chain;
}
例如下面这张图将创建4个chain链表节点,同时开辟了一个连续的缓冲区,并把这个缓冲区分割成4部分。
图:chain缓冲区链表结构
四、合并两个chain链表
//将in链表插入到chain链表末尾
ngx_int_t ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in)
{
ngx_chain_t *cl, **ll;
ll = chain;
//找到chain链表的最后一个节点
for (cl = *chain; cl; cl = cl->next)
{
ll = &cl->next;
}
//依次将in链表中的节点插入到chain链表末尾
while (in)
{
cl = ngx_alloc_chain_link(pool);
if (cl == NULL)
{
return NGX_ERROR;
}
cl->buf = in->buf;
*ll = cl;
ll = &cl->next;
in = in->next;
}
*ll = NULL;
return NGX_OK;
}
将in链表插入到chain链表末尾的布局如下:
图:合并链表
五、更新free链表
ngx_chain_update_chains函数会将busy链表中的空闲节点回收到free链表中
//将out链表插入到busy链表尾部,同时将合并后的链表从头开始的所有没有使用的节点,插入到空闲链表。
//合并链表后,out为null
void ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy,
ngx_chain_t **out, ngx_buf_tag_t tag)
{
ngx_chain_t *cl;
if (*busy == NULL)
{
//busy直接指向out链表
*busy = *out;
}
else
{
//找到busy链表的最后一个节点
for (cl = *busy; cl->next; cl = cl->next)
{
}
//将out链表插入到busy链表末尾
cl->next = *out;
}
*out = NULL;
while (*busy)
{
//合并后的该busy链表节点有内容时,则表示剩余节点都有内容,则退出
if (ngx_buf_size((*busy)->buf) != 0)
{
break;
}
//如果该busy链表节点不属于tag指向的模块,则跳过。
if ((*busy)->buf->tag != tag)
{
*busy = (*busy)->next;
continue;
}
//重置buf缓冲区所有空间都可用
(*busy)->buf->pos = (*busy)->buf->start;
(*busy)->buf->last = (*busy)->buf->start;
//将该空闲空闲区加入到free链表表头
cl = *busy;
*busy = cl->next;
cl->next = *free;
*free = cl;
}
}
这个函数将执行两个操作:
1、将out链表插入到busy链表的末尾,效果图如下,其中busy链表中的第一个节点表示该节点空间不在使用了,可以回收到free链表
2、将合并后的chain链表中,不在使用的节点回收到free链表中。例如上图在合并后的chain链表中的第一个节点将回收到free链表,效果图如下:
、