內存池函數:
ngx_create_pool
ngx_destroy_pool
ngx_reset_pool
ngx_palloc
ngx_pnalloc
ngx_palloc_block
ngx_palloc_large
ngx_pool_cleanup_add
內存池創建
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);//按照16字節對齊分配一塊size大小內存
if (p == NULL) {
return NULL;
}
p->d.last = (u_char *) p + sizeof(ngx_pool_t); //last指向數據部分開始(跳過header ngx_pool_t)結構
p->d.end = (u_char *) p + size; //end指向末尾結構
p->d.next = NULL; //next是對小塊內存鏈表管理指針
p->d.failed = 0; //此塊內存分配失敗次數
size = size - sizeof(ngx_pool_t); //去掉ngx_pool_t內存池的實際大小
p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;//最大內存池節點大小爲4095
p->current = p; //current指向p
p->chain = NULL;
p->large = NULL;
p->cleanup = NULL;
p->log = log;
return p;
}
其中初始化結構如下圖:
綠色線表示是結構體成員,比如ngx_pool_data_t的成員包括 last、end、next、failed
黑色線表示指針指向的地址
1 max爲創建size-sizeof(ngx_pool_t)和4095之間選取最小的
2 log在main中調用ngx_log_init創建的ngx_log_t結構(即全局的ngx_log對象)
原理:
nginx實際從該內存池分配空間的起始位置從d.last開始,隨着內存池空間的對外分配,這個字段的指向會後移,
內存池分配
void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
u_char *m;
ngx_pool_t *p;
//如果申請的內存大小小於創建的內存池節點大小(當然是去掉ngx_pool_t後)
if (size <= pool->max) {
p = pool->current;
do {
m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);//首先對齊last指針
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);
}
return ngx_palloc_large(pool, size);
}
ngx_palloc()嘗試從pool內存池裏分配size大小的空間,這有兩種情況。
第一種如果分配的size小於ngx_pool_t.max(小塊分配,即代碼127-140),首先需要把last 16位對齊,如果申請還存在空間,那麼就用這一內存節點(即代碼130)同時移動last指向分配後的地址,同時把申請的內存返回給調用着,如果從current遍歷小塊內存節點都沒有可以分配的空間(即遍歷鏈表一直到內存池節點next爲空)。就需要分配內存池節點調用ngx_palloc_block(pool,size)
此函數主要分配一個內存池節點,然後把這個節點插入到鏈表尾部,同時更新pool->current指針,更新算法是遍歷鏈表,如果發現這個節點已經分配失敗6次了,就把current指向下一個節點,如果所有節點都失敗6次以上,current指向新分配節點。
核心代碼如下:
current = pool->current;//初始化
for (p = current; p->d.next; p = p->d.next) {//遍歷
if (p->d.failed++ > 4) {//分配超過6次
current = p->d.next;
}
}
p->d.next = new;//插入新節點
pool->current = current ? current : new;//更新current
第二種情況,分配大塊內存直接調用malloc分配,分配完,遍歷pool->large鏈表,如果存在大塊內存池節點存在alloc爲空,那麼就把新分配的內存掛到這個節點上,如果沒有這樣的節點,那麼就分配插入p->large的鏈表頭部。
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;
//遍歷大塊內存鏈表的前幾個節點,看是否有釋放的節點,如果有就掛在上去
for (large = pool->large; large; large = large->next) {
if (large->alloc == NULL) {
large->alloc = p;
return p;
}
if (n++ > 3) {
break;
}
}
//如果沒有,那麼就分配一個節點,然後插入頭部節點
large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
if (large == NULL) {
ngx_free(p);
return NULL;
}
large->alloc = p;
large->next = pool->large;
pool->large = large;
return p;
}
清除回調函數
其中每個節點都是
struct ngx_pool_cleanup_s {
ngx_pool_cleanup_pt handler;//回調handler
void *data; //回調函數參數
ngx_pool_cleanup_t *next; //指向下一個節點
};
ngx_pool_cleanup_t *
ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
{
ngx_pool_cleanup_t *c;
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;
}