一、简介
最新稳定版本nginx1.20.2。
为了能高效、快速的分配内存,以及减少内存碎片等,nginx实现了自己的内存池基础组件。
主要实现文件ngx_palloc.h, ngx_palloc.c
二、数据结构
2.1 内存池主要结构
typedef struct {
u_char *last;
u_char *end;
ngx_pool_t *next;
ngx_uint_t failed;
} ngx_pool_data_t;
struct ngx_pool_s {
ngx_pool_data_t d;
size_t max;
ngx_pool_t *current;
ngx_chain_t *chain;
ngx_pool_large_t *large;
ngx_pool_cleanup_t *cleanup;
ngx_log_t *log;
};
- 内存池中第一个成员是一个结构体:
使用ngx_pool_data_t结构体来表示当前内存池信息。
last :下次开始分配的地址
end: 内存池的结束地址
next: 内存池链表,将多个内存池连接起来 - max
整个内存池的最大大小 - current
指向从当前内存池开始查找可用内存 - chain
buffer使用的,这里不涉及 - large
当需要的内存大于内存池最大大小时,需要通过malloc直接分配,然后形成链表进行组织 - cleanup
清理工作的回调链表 - log
日志句柄
2.2 大内存链
当需要分配的内存比内存池的最大大小都大时,内存池无法满足分配,所以直接从系统中分配,然后构成一个链表进行维护。
typedef struct ngx_pool_large_s ngx_pool_large_t;
struct ngx_pool_large_s {
ngx_pool_large_t *next;
void *alloc;
};
2.3 清理任务链
有一个回调任务的链表,当内存池销毁时,将依次遍历此链表,逐一回调handler进行清理工作。
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;
ngx_pool_cleanup_t *next;
};
三、内存结构图
3.1 逻辑
3.2 实际
可以看出,很多节点都是从内存池中分配的,所以可以把精力都放在实际的数据上而不必在意其他细节上。
四、实现
4.1 创建内存池
/*
* NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
* On Windows NT it decreases a number of locked pages in a kernel.
*/
#define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1)
#define NGX_DEFAULT_POOL_SIZE (16 * 1024)
ngx_pool_t *
ngx_create_pool(size_t size, ngx_log_t *log)
{
ngx_pool_t *p;
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;
}
从代码中可以看到,内存池最大不超过pagesize的大小
4.2 从内存池中分配空间
分配函数分了内存对齐和内存不对齐,但这只控制了内存池中分配空间,不控制大内存分配。
(1)分配小空间
- 内存对齐
ngx_palloc
- 内存不对齐
ngx_pnalloc
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);
}
当需要分配的空间小于max时,将使用小内存分配方式(即从内存池中分配空间),而ngx_pnalloc和ngx_palloc相比只是调用ngx_palloc_small时的最后一个参数为0。
- 从pool->current指向的内存池开始遍历,寻找满足分配大小的空间,找到则返回首地址
static ngx_inline vo