栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Nginx(5):Buffer缓冲区设计

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Nginx(5):Buffer缓冲区设计

刚那个长得像deque的链表需要找它的应用场景,缓冲区就不用我多说了吧,前前后后也看了好几个缓冲区的实现,谁的好,就拿来用咯。


文章目录
    • 缓冲区 Buffer
      • ngx_chain_t
    • 方法配置
      • 申请临时缓冲区
      • 分配 chain 节点
      • 分配一个 buf 链表
      • 合并buf链表
      • 从free chain链中获取一个空闲buf
      • 回收链表空间

缓冲区 Buffer

此前研究了 muduo 的缓冲区设计,有些心得,再来看看 nginx 的缓冲区设计。

typedef struct ngx_buf_s  ngx_buf_t;

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_file_t      *file;	//引用的文件
    ngx_buf_t       *shadow;//影子缓冲区,不怎么用,用的时候再说


    
    unsigned         temporary:1;

    
    unsigned         memory:1;

    
    unsigned         mmap:1;

    unsigned         recycled:1;	//可回收
    unsigned         in_file:1;		//处理的是文件
    unsigned         flush:1;		//需要刷新
    unsigned         sync:1;		//有些框架代码在sync为1时可能会有阻塞的方式进行I/O操作,它的意义视使用它的Nginx模块而定
    unsigned         last_buf:1;	//因为 ngx_buf_t可以由 ngx_chain_t链表串联起来。last_buf为1时,表示当前是最后一块待处理的缓冲区
    unsigned         last_in_chain:1;	//是否是 ngx_chain_t中的最后一块缓冲区
    unsigned         last_shadow:1;
    unsigned         temp_file:1;

     int   num;
};

好长哈,而且不知道为什么要来个 typedef,是历史遗留原因?

这是个公用的缓冲区吗?嗯,是从内存池中去申请空间的。

看来需要去完整的看一下 nginx 内存池的设计了,那个会比较重要些吧。


ngx_chain_t

ngx_chain_t是与ngx_buf_t配合使用的链表数据结构。

typedef struct ngx_chain_s       ngx_chain_t;		//又来
struct ngx_chain_s {
    ngx_buf_t    *buf;	//指向当前的ngx_buf_t缓冲区
    ngx_chain_t  *next;	//来指向下一个ngx_chain_t。
    //如果这是最后一个 ngx_chain_t,则需要把next置为NULL。
};

buf指向当前的ngx_buf_t缓冲区,next则用来指向下一个ngx_chain_t。如果这是最后一个 ngx_chain_t,则需要把next置为NULL。 在向用户发送HTTP包体时,就要传入ngx_chain_t链表对象,注意,如果是最后一个 ngx_chain_t,那么必须将next置为NULL,否则永远不会发送成功,而且这个请求将一直不会 结束(Nginx框架的要求)。

(包头存在链表里面)


方法配置 申请临时缓冲区
//申请临时缓冲区
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;
}

临时的哈,用完还得还的哈。


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

分配一个 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);
    if (p == NULL) {
        return NULL;
    }

    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 = cl;
        ll = &cl->next;
    }

    *ll = NULL;

    return chain;
}

合并buf链表

合并buf链表,将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;
}

从free chain链中获取一个空闲buf
ngx_chain_t *ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free)	//@param free 待查找chain链
{
    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链表中或者内存池中。


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) {  }

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

你悟到了吗?

这么大块空间,收收发发的不累吗?既然都被申请过了,那就不要收回来了,就放外面吧,下次要用直接拿去。


关于文件操作的我就先不说了哈。。

歇会儿去看内存池去了哈哈

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/277541.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号