上一篇介绍的是ngx_pool_t内存池,对于Nginx来说万物皆池化,接下来介绍的ngx_buf_t也是从内存池中获取内存空间。
一、数据结构定义
1.1、数据结构定义
ngx_buf_t从名字上可知,是缓冲区的意思。与大多数开源软件定义的缓冲区有点不同,它不仅仅能够处理内存,还能够处理文件,输入结构如下:
typedef void * ngx_buf_tag_t;
typedef struct ngx_buf_s ngx_buf_t;
struct ngx_buf_s {
u_char *pos; /* 待处理缓冲区起始位置 */
u_char *last;/* 待处理缓冲区最后一个位置 last - pos = 实际有效内容 */
off_t file_pos; /* 文件处理 起始位置 */
off_t file_last; /* 文件处理 最后一个位置 */
u_char *start; /* start of buffer 缓冲区起始地址 */
u_char *end; /* end of buffer 缓冲区结束地址 */
ngx_buf_tag_t tag; /* 使用buf的模块设置,通常设置为函数指针 */
ngx_file_t *file; /* 当buf指向文件时,设置文件结构 */
ngx_buf_t *shadow; /* 影子缓冲区 为了节约内存不会重新申请一块内存 而是只申请一个头部信息 */
/* the buf's content could be changed */
unsigned temporary:1; /* buf指向临时内存 内存数据可以进行修改 */
/*
* the buf's content is in a memory cache or in a read only memory
* and must not be changed
*/
unsigned memory:1; /* buf指向内存数据 只读 */
/* the buf's content is mmap()ed and must not be changed */
unsigned mmap:1; /* buf指向共享内存map 数据是只读 */
unsigned recycled:1; /* 表示可重复使用 */
unsigned in_file:1; /* 表示buf指向文件 */
unsigned flush:1; /* 表示进行flush操作 */
unsigned sync:1; /* 是否需要进行同步操作 会阻塞Nginx进程 */
unsigned last_buf:1; /* 表示是否为最后一个buf 需要结合ngx_chain_t */
unsigned last_in_chain:1; /* 表示在chain中是否为最后一个buf */
unsigned last_shadow:1; /* 最后一个影子buf */
unsigned temp_file:1; /* 是否为临时文件 */
/* STUB */ int num;
};
通过定义可知道,ngx_buf_t是同时支持内存和文件的。但有时候只定义一个buf是无法满足业务需求,这时候需要就需要把多个buf关联起来。Nginx中通过ngx_chain_t通过单链表方式将其组织起来,定义如下:
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};
1.2、使用场景
ngx_buf_t最常见的使用场景,接收/发送报文体。
二、相关接口
Nginx中内存基本都是来自内存池--万物皆池化。这里ngx_buf_t自然也是需要从内存池中申请资源。接下来介绍一下相关接口
#define ngx_alloc_buf(pool) ngx_palloc(pool, sizeof(ngx_buf_t)) //申请buf头
#define ngx_calloc_buf(pool) ngx_pcalloc(pool, sizeof(ngx_buf_t))//申请buf头并初始化为0
/**
* 申请一个buf,类型为temporary
* @param pool 内存池
* @param size 当前buf大小
*/
ngx_buf_t *
ngx_create_temp_buf(ngx_pool_t *pool, size_t size)
{
ngx_buf_t *b;
b = ngx_calloc_buf(pool);//先申请一个buf头部
if (b == NULL) {
return NULL;
}
b->start = ngx_palloc(pool, size);//buf内容体
if (b->start == NULL) {//申请失败
return NULL;
}
/*
* set by ngx_calloc_buf():
*
* b->file_pos = 0;
* b->file_last = 0;
* b->file = NULL;
* b->shadow = NULL;
* b->tag = 0;
* and flags
*/
//设置各类指针
b->pos = b->start;
b->last = b->start;
b->end = b->last + size;
b->temporary = 1;//标记为临时内存
return b;
}
/**
* 分配一个chain节点
* @param pool 内存池
*/
ngx_chain_t *
ngx_alloc_chain_link(ngx_pool_t *pool)
{
ngx_chain_t *cl;
cl = pool->chain;
/* Nginx为了提升效率,会把已经使用过ngx_chain_t保存到ngx_pool_t中以便下次使用 */
if (cl) {
pool->chain = cl->next;
return cl;
}
cl = ngx_palloc(pool, sizeof(ngx_chain_t));
if (cl == NULL) {
return NULL;
}
return cl;
}
/**
* 分配一个buf链表
* @param pool 内存池
* @param bufs buf集合
*/
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;
p = ngx_palloc(pool, bufs->num * bufs->size);//申请buf内容体
if (p == NULL) {
return NULL;
}
ll = &chain;
for (i = 0; i < bufs->num; i++) {
b = ngx_calloc_buf(pool);//为num个buf,申请buf头
if (b == NULL) {
return NULL;
}
/*
* set by ngx_calloc_buf():
*
* b->file_pos = 0;
* b->file_last = 0;
* b->file = NULL;
* b->shadow = NULL;
* b->tag = 0;
* and flags
*
*/
b->pos = p;
b->last = p;
b->temporary = 1;
b->start = p;
p += bufs->size;//调整p指针
b->end = p;
cl = ngx_alloc_chain_link(pool);//申请ngx_chain_t 组织链表
if (cl == NULL) {
return NULL;
}
cl->buf = b;
*ll = cl;//插入链表
ll = &cl->next;
}
*ll = NULL;
return chain;
}
/**
* 合并buf链表 将in链表合并到chain中
* @param pool 内存池
* @param chain 输出参数
* @param in 输入参数
*/
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
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;
}
/**
* 从chain链中获取一个空闲buf
* @param pool 内存池
* @param free 待查找chain链
*/
ngx_chain_t *
ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free)
{
ngx_chain_t *cl;
if (*free) {//如果链表不空 则表示空闲 所以直接取一个节点即可返回
cl = *free;
*free = cl->next;
cl->next = NULL;
return cl;
}
//free链表为空 则需要从内存池中分配一个新的节点
cl = ngx_alloc_chain_link(p);
if (cl == NULL) {
return NULL;
}
cl->buf = ngx_calloc_buf(p);
if (cl->buf == NULL) {
return NULL;
}
cl->next = NULL;
return cl;
}
/**
* 更新chain链表 释放内存 将busy中空闲节点回到free链表中或者内存池中
* @param p 内存池
* @param free 空闲链
* @param busy 正在使用链
* @param out 将out中分发到 free或者busy中
* @param tag 标志 分发原则 一般是函数指针
*/
void
ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, ngx_chain_t **busy,
ngx_chain_t **out, ngx_buf_tag_t tag)
{
ngx_chain_t *cl;
/* 将out中所有节点挂到busy中 */
if (*out) {
if (*busy == NULL) {
*busy = *out;
} else {
for (cl = *busy; cl->next; cl = cl->next) { /* void */ }
cl->next = *out;
}
*out = NULL;
}
while (*busy) {
cl = *busy;
if (ngx_buf_size(cl->buf) != 0) {//表示当前buf有尚未处理的数据 直接退出
break;
}
/**
* 如果tag不同(可以理解成该buf是由那个模块创建),则回到内存池中
*/
if (cl->buf->tag != tag) {
*busy = cl->next; //处理下一个节点
ngx_free_chain(p, cl); //会受到内存池总
continue;
}
/* tag相同 表示相同模块申请的buf 因此回收到free链表中 */
cl->buf->pos = cl->buf->start;
cl->buf->last = cl->buf->start;
*busy = cl->next;
cl->next = *free;
*free = cl;
}
}
三、总结
基本上把所有与ngx_buf_t、ngx_chain_t相关的接口都介绍了一遍,还是比较简单,逻辑并不复杂。下一篇介绍ngx_list_t