ngx_buf_t和ngx_chain_t是nginx中操作内存的重要手段, 很多的数据都需要通过这个结构进行保存. 其中ngx_buf_t中保存一块可用内存, ngx_chain_t则是将内存块连接起来,组成一个链表, 操作这两个数据结构函数并不多, 下面进行了详解.
基本数据结构
typedef struct ngx_chain_s ngx_chain_t;
typedef struct ngx_buf_s ngx_buf_t;
struct ngx_buf_s {
/*当前读取buffer位置*/
u_char *pos;
/*实际占用buffer的最后位置*/
u_char *last;
/*当前读取文件位置*/
off_t file_pos;
/*文件最后位置*/
off_t file_last;
/*buffer开始位置*/
u_char *start;
/*buffer结束位置*/
u_char *end;
/*内存标记*/
ngx_buf_tag_t tag;
/*文件句柄*/
ngx_file_t *file;
ngx_buf_t *shadow;
/*1表示内存可以修改*/
unsigned temporary:1;
/*1表示内存不可以修改*/
unsigned memory:1;
/*1表示操作内容通过mmap()函数映射不可修改*/
unsigned mmap:1;
/*1表示内存可以回收*/
unsigned recycled:1;
/*1表示操作内容在文件中*/
unsigned in_file:1;
/*1表示立即将内容发送出去*/
unsigned flush:1;
/*1表示立即将内容同步发送出去*/
unsigned sync:1;
/*标记chain是否为最后一块buf*/
unsigned last_buf:1;
/*标记chain中是否为最后一个chain,最后一块buf并不表示是最后一个chain,但最后一个chain一定是最后一块buf*/
unsigned last_in_chain:1;
unsigned last_shadow:1;
unsigned temp_file:1;
/* STUB */ int num;
};
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};
操作函数
创建一个buf很简单, 直接在内存池中分配一块内存, 然后分配size大小空间, 给ngx_buf_t各个字段进行赋值即可.
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;
}
创建一个buf链表节点, 首先检查内存池pool->chain缓存中是否为空, 不为空直接取出使用, 为空重新分配.
ngx_chain_t *
ngx_alloc_chain_link(ngx_pool_t *pool)
{
ngx_chain_t *cl;
cl = pool->chain;
if (cl) {
pool->chain = cl->next;
return cl;
}
cl = ngx_palloc(pool, sizeof(ngx_chain_t));
if (cl == NULL) {
return NULL;
}
return cl;
}
直接创建一个ngx_buf_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;
/*首先分配所有buf需要的一整块内存, 没有循环分配小内存, 显然为了提高速度, 处处体现了作者对代码质量和性能的要求之高*/
p = ngx_palloc(pool, bufs->num * bufs->size);
if (p == NULL) {
return NULL;
}
/*这里用一个二级指针保存第一个节点指针地址, 同样也保存后面节点指针地址, 为什么这么干, 每次保存下上次节点也可以实现,
*这么干少了一次循环内的判断, 否则我们需要判断出第一次循环, 并给chain赋值
*/
ll = &chain;
for (i = 0; i < bufs->num; i++) {
b = ngx_calloc_buf(pool);
if (b == NULL) {
return NULL;
}
b->pos = p;
b->last = p;
b->temporary = 1;
b->start = p;
p += bufs->size;
b->end = p;
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) {
return NULL;
}
cl->buf = b;
/*ll保存的上一个节点指针的指针, 赋值相当于给上一个节点next指针赋值*/
*ll = cl;
/*取本节点next指针地址, 用于下次循环赋值使用*/
ll = &cl->next;
}
*ll = NULL;
return chain;
}
buf链表添加或者copy, 我觉得这里叫append(附加)可能更好, 因为只是将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;
for (cl = *chain; cl; cl = cl->next) {
ll = &cl->next;
}
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;
}
更新链表函数功能是回收busy和out链表中闲置资源, 如果tag标志位与传入的一致, 则链表回收到free链表中, 否则直接释放会pool中, 多用于过滤模块
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;
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) {
break;
}
if (cl->buf->tag != tag) {
*busy = cl->next;
ngx_free_chain(p, cl);
continue;
}
cl->buf->pos = cl->buf->start;
cl->buf->last = cl->buf->start;
*busy = cl->next;
cl->next = *free;
*free = cl;
}
}