1、内存池关键数据结构
1.1 描述内存池的总数据结构
// 内存池数据结构
struct ngx_pool_s
{
ngx_pool_data_t d; // 内存池的数据区域
size_t max; // 最大每次可分配内存
ngx_pool_t *current; // 指向当前的内存池指针地址。ngx_pool_t链表上最后一个缓存池结构
ngx_chain_t *chain; // 缓冲区链表
ngx_pool_large_t *large; // 存储大数据的链表
ngx_pool_cleanup_t *cleanup; // 可自定义回调函数,清除内存块分配的内存
ngx_log_t *log; // 日志
}
1.2 内存池数据区描述数据结构
// 把一个未命名的结构直接取名ngx_pool_data_t
typedef struct
{
u_char *last; // 该内存池内,可用内存的起始地址
u_char *end; // 该内存池内,内存池结束地址
ngx_pool_t *next; // 指向下一个内存池的指针
ngx_uint_t failed; // 失败次数,具体含义还未知
} ngx_pool_data_t;
1.3 内存池大块内存描述数据结构
// 大内存块描述
struct ngx_pool_large_s
{
ngx_pool_large_t *next; // 指向下一个large节点
void *alloc; // 指向大内存块地址指针
};
1.4 可自定义清除函数内存描述
// typedef定义函数指针
// 给变量类型定义一个别名,
typedef void (*ngx_pool_cleanup_pt)(void *data);
typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t;
struct ngx_pool_cleanup_s
{
ngx_pool_cleanup_pt handler; // 清除函数指针
void *data; // 内存指针,作为handler的入参
ngx_pool_cleanup_t *next; // 指向下一个cleanup节点
};
1.5 内存池原理
内存池是一个链表,链表的每个节点上都有分配空间,前面部分空间用于存储『ngx_pool_s』结构自身数据,剩下空间、链表通过此结构进行管理。
『ngx_pool_data_t』结构记录了当前节点可用内存地址的起(last)、止(end)地址以及下一个内存池节点,从而形成链表。
当申请的空间大于max时,会按大块内存进行分配,用使用『ngx_pool_large_s』进行管理,也是形成链表。
如图:
2、关键函数
2.1 申请/销毁内存基础函数(内存在堆上)
文件路径:src/os/unix/ngx_alloc.c
void *ngx_alloc(size_t size, ngx_log_t *log)
{
void *p;
p = malloc(size);
if (p == NULL)
{
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
"malloc(%uz) failed", size);
}
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size);
return p;
}
void *ngx_calloc(size_t size, ngx_log_t *log)
{
void *p;
p = ngx_alloc(size, log);
if (p)
{
ngx_memzero(p, size);
}
return p;
}
2.2 创建内存池节点
文件路径:src/core/ngx_palloc.c
ngx_pool_t *
ngx_create_pool(size_t size, ngx_log_t *log)
{
ngx_pool_t *p;
// malloc内存,大小为size
p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
if (p == NULL)
{
return NULL;
}
// 可用内存空间起始地址
p->d.last = (u_char *)p + sizeof(ngx_pool_t);
// 可用内存空间结束地址
p->d.end = (u_char *)p + size;
// 下一个内存池节点指针
p->d.next = NULL;
p->d.failed = 0;
size = size - sizeof(ngx_pool_t);
// 设置本内存池最大可用空间
p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
// 当前内存池节点指针
p->current = p;
p->chain = NULL;
p->large = NULL;
p->cleanup = NULL;
p->log = log;
return p;
}
2.3 销毁内存池
共销毁三个区域:
- cleanup存储区,调用自定义的清理函数完成释放,全链表完成释放。
- large区域,直接free掉,全链表完成释放
- 内存池节点内存销毁,全链表完成释放。
void ngx_destroy_pool(ngx_pool_t *pool)
{
ngx_pool_t *p, *n;
ngx_pool_large_t *l;
ngx_pool_cleanup_t *c;
for (c = pool->cleanup; c; c = c->next)
{
if (c->handler)
{
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"run cleanup: %p", c);
c->handler(c->data);
}
}
#if (NGX_DEBUG)
/*
* we could allocate the pool->log from this pool
* so we cannot use this log while free()ing the pool
*/
for (l = pool->large; l; l = l->next)
{
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);
}
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next)
{
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"free: %p, unused: %uz", p, p->d.end - p->d.last);
if (n == NULL)
{
break;
}
}
#endif
for (l = pool->large; l; l = l->next)
{
if (l->alloc)
{
ngx_free(l->alloc);
}
}
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next)
{
ngx_free(p);
if (n == NULL)
{
break;
}
}
}
2.3 重置内存池
- large内存区域,全链表释放。
- 全链表更新内存池节点last指针到初始位置。
void ngx_reset_pool(ngx_pool_t *pool)
{
ngx_pool_t *p;
ngx_pool_large_t *l;
for (l = pool->large; l; l = l->next)
{
if (l->alloc)
{
ngx_free(l->alloc);
}
}
for (p = pool; p; p = p->d.next)
{
p->d.last = (u_char *)p + sizeof(ngx_pool_t);
p->d.failed = 0;
}
pool->current = pool;
pool->chain = NULL;
pool->large = NULL;
}
2.4 使用内存池分配空间
如果申请的内存空间小于max值,则走小内存空间申请,反之走大块内存申请。
void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
if (size <= pool->max)
{
return ngx_palloc_small(pool, size, 1);
}
#endif
return ngx_palloc_large(pool, size);
}
2.4.1 小块内存申请
如果当前内存池中有size长度的空间,则直接分配,否则进行扩容操作,新申请一个节点
static ngx_inline void *
ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)
{
u_char *m;
ngx_pool_t *p;
p = pool->current;
// 如果当前内存池中有size长度的空间可用,直接返回,否则创建新的内存池节点,进行扩容
do
{
m = p->d.last;
if (align)
{
m = ngx_align_ptr(m, NGX_ALIGNMENT);
}
if ((size_t)(p->d.end - m) >= size)
{
p->d.last = m + size;
return m;
}
p = p->d.next;
} while (p);
// 扩容,开辟新的内存池节点
return ngx_palloc_block(pool, size);
}
2.4.2 新建内存池节点
- 新申请一个节点,为本次申请提供空间。
- 新申请节点接入链表中。
- 需要注意的是,如果从当前pool申请内存失败超过4次,则current跳转到当前链表的最后一个节点,同时将新申请的节点挂载到链表尾。
static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new;
psize = (size_t)(pool->d.end - (u_char *)pool);
m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
if (m == NULL)
{
return NULL;
}
new = (ngx_pool_t *)m;
new->d.end = m + psize;
new->d.next = NULL;
new->d.failed = 0;
m += sizeof(ngx_pool_data_t);
m = ngx_align_ptr(m, NGX_ALIGNMENT);
// 将last指针向下移动,跳过本次申请占用的部分
new->d.last = m + size;
for (p = pool->current; p->d.next; p = p->d.next)
{
// 这里体现的应该是当前pool节点以及其后面节点内存不足了
// 而且如果失败大于4次,则移动current到当前链表最后一个节点
if (p->d.failed++ > 4)
{
pool->current = p->d.next;
}
}
p->d.next = new;
return m;
}
2.4.3 申请大内存快
- 申请内存空间。
- 判断large链表上前3个节点是否有释放空间节点(large->next为null),如果没有则将新申请的节点插入到表头。
static void *
ngx_palloc_large(ngx_pool_t *pool, size_t size)
{
void *p;
ngx_uint_t n;
ngx_pool_large_t *large;
// 开辟内存空间
p = ngx_alloc(size, pool->log);
if (p == NULL)
{
return NULL;
}
n = 0;
// 查找large链表上是否有释放节点,最多查找3个
for (large = pool->large; large; large = large->next)
{
if (large->alloc == NULL)
{
large->alloc = p;
return p;
}
if (n++ > 3)
{
break;
}
}
// 分配large结构
large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
if (large == NULL)
{
ngx_free(p);
return NULL;
}
// 将large结构插入到pool current节点的large节点之后
large->alloc = p;
large->next = pool->large;
pool->large = large;
return p;
}
2.5 大内存块释放
释放指定的大内存块。
ngx_int_t
ngx_pfree(ngx_pool_t *pool, void *p)
{
ngx_pool_large_t *l;
for (l = pool->large; l; l = l->next)
{
if (p == l->alloc)
{
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"free: %p", l->alloc);
ngx_free(l->alloc);
l->alloc = NULL;
return NGX_OK;
}
}
return NGX_DECLINED;
}
2.6 分配cleanup内存块
- 申请内存块。
- 申请ngx_pool_cleanup_t结构。
- 将该结构插入到当前pool->cleanup后面
ngx_pool_cleanup_t *
ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
{
ngx_pool_cleanup_t *c;
// 开辟ngx_pool_cleanup_t结构存储空间
c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));
if (c == NULL)
{
return NULL;
}
if (size)
{
// 开辟存储空间
c->data = ngx_palloc(p, size);
if (c->data == NULL)
{
return NULL;
}
}
else
{
c->data = NULL;
}
// 自定义清理函数指针初始化
c->handler = NULL;
// 将存储结构插入链表中
c->next = p->cleanup;
p->cleanup = c;
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);
return c;
}
2.7 自定义函数清理文件
使用自定义的handler清理内存(主要是文件)
void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd)
{
ngx_pool_cleanup_t *c;
ngx_pool_cleanup_file_t *cf;
for (c = p->cleanup; c; c = c->next)
{
if (c->handler == ngx_pool_cleanup_file)
{
cf = c->data;
if (cf->fd == fd)
{
c->handler(cf);
c->handler = NULL;
return;
}
}
}
}
2.8 关闭文件回调函数
void ngx_pool_cleanup_file(void *data)
{
ngx_pool_cleanup_file_t *c = data;
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d",
c->fd);
if (ngx_close_file(c->fd) == NGX_FILE_ERROR)
{
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", c->name);
}
}
2.9 删除文件回调函数
void ngx_pool_delete_file(void *data)
{
ngx_pool_cleanup_file_t *c = data;
ngx_err_t err;
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d %s",
c->fd, c->name);
if (ngx_delete_file(c->name) == NGX_FILE_ERROR)
{
err = ngx_errno;
if (err != NGX_ENOENT)
{
ngx_log_error(NGX_LOG_CRIT, c->log, err,
ngx_delete_file_n " \"%s\" failed", c->name);
}
}
if (ngx_close_file(c->fd) == NGX_FILE_ERROR)
{
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", c->name);
}
}