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.内存块链表管理,提高内存碎片利用率,避免大块内存的分配。