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;
    }
}