Nginx源码初探之数据结构 - 缓冲区结构

        Nginx通过内存池使用内存,需要从内存池中申请资源在缓存使用上。可以对零散的缓冲区碎片进行了统一的管理和再分配,从而提高了缓冲区的利用率。其中最底层的数据结构,就是内存块数据结构和内存块链表数据结构。

1.数据结构

        ngx_buf_t是Nginx系统中系统分配内存的基本单位。ngx_buf_t分为三部分,内存块部分,文件部分,元数据,并分别提供了控制机制。也就是说Nginx内存分配时,它可以是指向内存中的某个缓冲区,也可能指向文件的某一个部分,也可能是一些元数据,数据结构如下。

typedef void *            ngx_buf_tag_t;

typedef struct ngx_buf_s  ngx_buf_t;
/**
 * Nginx缓冲区
 */
struct ngx_buf_s {
    u_char          *pos;           /* 处理数据时,数据在一个内存块的起始位置  */
    u_char          *last;          /* 处理数据时,数据在一个内存块的结束位置 */
    off_t            file_pos;		/* 处理文件时,文件起始位置在文件中的偏移量  */
    off_t            file_last;		/* 处理文件时,文件结束位置在文件中的偏移量  */
 
    u_char          *start;         /* 处理缓冲区数据时,数据在缓冲区开始的指针地址(针对buf) */
    u_char          *end;           /* 处理缓冲区数据时,数据在缓冲区结束的指针地址(针对buf) */
    ngx_buf_tag_t    tag;			/* void类型的指针 */
    ngx_file_t      *file;			/* 处理文件时,引用的文件 */
    ngx_buf_t       *shadow;        /* 指向数据来源,可以是内存的某个缓冲区,也可以是文件的某一个部分,或者是元数据 */
 
 
    /* the buf's content could be changed */
 
    unsigned         temporary:1;	 /* 内存修改状态位,为1时内存不可修改 */
 
    /*
     * the buf's content is in a memory cache or in a read only memory
     * and must not be changed
     */
    unsigned         memory:1;   	/* 数据在缓存中或者只读缓存时,不可被修改 */
 
    /* the buf's content is mmap()ed and must not be changed */
    unsigned         mmap:1;		/* 缓存映射过来的数据,不可修改 */
 
    unsigned         recycled:1;	/* 缓存中的数据不可回收 */
    unsigned         in_file:1;		/* 待处理数据是文件中的数据 */
    unsigned         flush:1;		/* 缓冲区中的数据不能进进行flush操作 */
    unsigned         sync:1;		/* 缓冲区中的数据不能进行同步操作 */
    unsigned         last_buf:1;	/*缓冲区链表上的最后一块待处理缓冲区 */
    unsigned         last_in_chain:1;/* 缓冲区链表上的最后一块缓冲区 */
 
    unsigned         last_shadow:1;	/* 最后一个缓存映射 */
    unsigned         temp_file:1;	/* 当前缓冲区是否属于临时文件 */
 
    /* STUB */ int   num;
};

        ngx_buf_t只能分配连续的内存地址区块,或者连续的文件内容,当需要分配较大的内存块或者文件内容时需要使用缓冲区链表结构 ngx_chain_t。通过ngx_chain_t链表结构进行关联和管理。当需要内存过大时,可以通过整合空闲的缓存碎片,将联系的内存空间放入同一个buf中,然后将多个buf串联起来构成一个较大的内存块。从而避免在内存中分配大块的内存空间,数据链表ngx_chain_t数据结构如下。

typedef struct ngx_chain_s           ngx_chain_t;//别名的定义在nginx_core.h中,

struct ngx_chain_s {
    ngx_buf_t    *buf;/* 缓冲区链表的数据单元 */
    ngx_chain_t  *next;/* 缓冲区链表的下一节点的指针*/
};

2.使用场景

最常见的使用场景是接受和发送报文。
3.相关接口
3.1分配内存

       分配内存重要的参数是内存池和分配空间的大小。先在内存池内搜索符合的内存块,如果有就直接复用。如果没有就重新分配内存块,然后设置内存块的起始位置和结束位置,并标记当前内存块不可修改。分配成功后将内存块放入内存池中详见ngx_palloc方法。

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

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

3.2分配缓存区链表节点
       缓冲区释放节点后将节点保存在内存池中ngx_pool_t,需要的时候会先在内存池中获取空闲碎片,没有的情况下再去内存池中分配。

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

Nginx内存分配的特点:
1.通过内存池分配,内存可以反复利用,避免频繁的创建和释放内存
2.内存块链表管理,提高内存碎片利用率,避免大块内存的分配。

发布了8 篇原创文章 · 获赞 0 · 访问量 120

猜你喜欢

转载自blog.csdn.net/mlydaemon/article/details/103918383