为了方便管理内存和文件等计算机资源,提高运行效率,nginx实现了自己的内存池。除了启动过程中在尚未创建内存池之前的阶段是直接使用系统的内存管理接口之外,nginx的内存管理都是通过内存池进行的。
nginx的内存池源码位于ngx_palloc.c和ngx_palloc.h中。首先看内存池相关的数据结构的定义:
struct ngx_pool_s {
ngx_pool_data_t d;//保存本节点内存分配信息
size_t max;//一次从内存池可以获取的小内存块的最大值,不超过pagesize - 1
ngx_pool_t *current;//指向内存池链表当前所在节点
ngx_chain_t *chain;//
ngx_pool_large_t *large;//指向从该内存池分配的大块内存链表的首结点
ngx_pool_cleanup_t *cleanup;//指向除了内存外其它已分配资源(打开的文件等)链表的首结点
ngx_log_t *log;//日志结构体指针,打印日志时使用
};
struct ngx_pool_data_t {
u_char *last;//已分配未使用的内存块的起始地址
u_char *end;//已分配内存的最后一个字节的地址
ngx_int_t failed;//在该结点上分配内存失败次数
ngx_pool_data_t *next;//指向内存池链表中的下一个结点
};
struct ngx_pool_large_t {
void *alloc;//已分配的大块内存的地址
ngx_pool_large_t *next;//链表中下一个结点的地址
};
struct ngx_pool_cleanup_s {
ngx_pool_cleanup_pt handler;//资源清理函数
void *data;//资源地址
ngx_pool_cleanup_s *next;//下一个待清理资源地址
};
typedef void (*ngx_pool_cleanup_pt)(void
*data);
Nginx的内存池是一个链表结构,链表的每个节点都是一个pool对象,从内存池分配出去的内存块实际位置是在各个pool对象的数据部分中,如下图所示
从上面的图中可以看到,每个节点由头和数据两个部分组成。头部实际上就是一个ngx_pool_t结构体,数据部分才是从内存池分配出去的内存块所在的位置。下图展示了内存池对象的详细布局
下面将介绍内存池的常用函数/宏定义的原型及使用:
ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log)
创建一个大小为size的pool对象,分配时以16字节为单位进行对齐,因此实际分配的内存大小可能会大于size。
ngx_destroy_pool(ngx_pool_t *pool)
销毁pool指向的内存池。包括执行pool的cleanup对象的回调函数,释放大内存块(large),以及小内存块
ngx_reset_pool(ngx_pool_t *pool)
重置pool。该函数会释放从内存池分配的大内存块,并将小内存块重置成初始状态,cleanup成员不做任何操作
void *ngx_palloc(ngx_pool_t *pool, size_t size)
从内存池获取大小为size的内存块,返回内存块的首地址。如果size大于内存池单次最大可分配小内存块,将会向操作系统申请一块大小为size的内存,并且将这块内存挂在pool的large链表中
void *ngx_pnalloc(ngx_pool_t *pool, size_t size)
与ngx_palloc一样,唯一的区别在于申请小块内存时会以默认大小进行内存对齐,所以实际返回的内存块大小可能大于size
void *ngx_pmemalign(ngx_pool_t *pool, size_t size,size_t alignment)
获取基础大小为size,以alignment为单位进行对齐的内存块,所分配内存块均挂在large成员中
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p)
释放pool的large成员中,数据地址为p的节点的内存块
void *ngx_pcalloc(ngx_pool_t *pool, size_t size)
与ngx_palloc基本相同,唯一区别在于会清空内存块
ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
向内存池中添加一个需要释放的资源.size表示需要从内存池申请的需要用来保存资源信息的内存块的大小(即ngx_pool_cleanup_t的data成员的大小)