菜鸟学习Nginx之ngx_buf_t

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xxb249/article/details/84309976

上一篇介绍的是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链表
 * @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链表 释放内存 分门别类
 * @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不一致 则将chain挂到pool中 */
        if (cl->buf->tag != tag) {
            *busy = cl->next;
            ngx_free_chain(p, cl);
            continue;
        }
        /* 修改指针并挂到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

 

猜你喜欢

转载自blog.csdn.net/xxb249/article/details/84309976