Ngxin–源码分析 缓冲区链表

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 Ngxin–源码分析 缓冲区链表,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

1.基本数据结构

在处理 TCP/HTTP 请求时会经常创建多个缓冲区来存放数据, Nginx缓冲区块简单地组织一个单向链表

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

buf:         缓冲区指针

next         下一个链表节点

注意:

  • ngx_chain_t是用来管理ngx_buf_t;
  • 内存池的pool->chain就是保存空闲的缓冲区链表的;
  • 通过链表的方式实现buf有一个非常大的好处:如果一次需要缓冲区的内存很大,那么并不需要分配一块完整的内存,只需要将缓冲区串起来就可以

Ngxin--源码分析 缓冲区链表

2.操作函数

释放

必须有一种结构来管理缓冲区(最好是链表),不然无法回收
内存池上的chain字段 ,被清空的ngx_chain_t结构都会放在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 c
}

ngx_free_chain()用来从内存池里获取释放ngx_chain_t对象,声明是:

#define ngx_free_chain(pool, cl)                                             \
    cl->next = pool->chain;                                                  \
    pool->chain = cl

创建多个缓冲区

由于ngx_chain_t在 Nginx里应用得很频繁,所以Nginx对此进行了优化。在内存池里保存了一个空闲ngx_chain_t链表,分配时从这个链表里摘取,释放时再挂上去。
typedef struct { 
ngx_int_t    num;
    size_t       size;
} ngx_bufs_t

1.节点数量

2.缓冲区的大小

// 创建多个buf
/*
    typedef struct {
        ngx_int_t    num;
        size_t       size;
    } ngx_bufs_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;

    // @param2  分配大小 = num * size
    p = ngx_palloc(pool, bufs->num * bufs->size);
    if (p == NULL) {
        return NULL;
    }

    ll = &chain;

    for (i = 0; i < bufs->num; i++) {

        b = ngx_calloc_buf(pool);
        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;
        b->end = p;

        cl = ngx_alloc_chain_link(pool);  // 创建链表结构
        if (cl == NULL) {
            return NULL;
        }

        cl->buf = b;

        // 尾插法
        // ll记录前一次循环的&cl->next
        // 下一次循环 *ll = cl 将 &cl->next 上的值改为 cl
        // 这样就将上一次循环的next指向当前循环的cl
        *ll = cl;
        ll = &cl->next; 
    }

    *ll = NULL;

    return chain;
}

编程技巧:尾插法(实现 cl=cl->next)

这里重点说下代码后部分的尾插法。
ll是二级指针
ll记录前一次循环的&cl->next (记录了这次循环的cl的next的地址)
下一次循环 *ll = cl 将 &cl->next 上的值改为 cl (改变了上一次循环的cl的next的地址上的值,也就是改变了上一次循环cl的next的值,就是改变了上一次循环的next,让其指向当前的cl)
这样就将上一次循环的next指向当前循环的cl

将其他缓冲区链表放到已有缓冲区链表的尾部

ngx_int_t ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,
    ngx_chain_t *in);

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

    for (cl = *chain; cl; cl = cl->next) {
        ll = &cl->next; // 拿到*chain链表最后一个节点的地址
    }

    while (in) {
        cl = ngx_alloc_chain_link(pool);
        if (cl == NULL) {
            *ll = NULL;
            return NGX_ERROR;
        }

        cl->buf = in->buf;
        *ll = cl;
        ll = &cl->next;
        in = in->next;
    }

    *ll = NULL;

    return NGX_OK;
』

获取一个空闲buf

 获取一个空闲buf
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;
    }

    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;

释放缓冲区链表

释放掉的cl归还到 pool->chain 上

/*
#define ngx_free_chain(pool, cl)                                             \
    cl->next = pool->chain;                                                  \
    pool->chain = cl
*/

// 释放缓冲区链表
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)  // free 空闲的  busy 忙碌的  out 待处理的
{
    ngx_chain_t  *cl;

    // 判断out应该放到什么位置
    // 如果busy == NULL 则*busy = *out
    // 否则将*out放到*busy的末尾
    if (*out) {
        if (*busy == NULL) {
            *busy = *out;

        } else {
            for (cl = *busy; cl->next; cl = cl->next) { /* void */ }

            cl->next = *out;
        }

        *out = NULL;
    }

    // 检查busy
    // 如果busy中的buf还存在需要处理的内存空间,则停止处理
    // 否则将buf置空(处理pos last)
    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;
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/129629.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!