首先,我們來看看它幾個主要的資料結構:
//ngx記憶體池的頭部資訊
struct ngx_pool_s ;
//ngx小塊記憶體的資訊
typedef struct ngx_pool_data_t;
很明顯,ngx_pool_data_t和ngx_pool_s構成了記憶體池的主題。在ngx_pool_data_t中:last表示記憶體池中未使用記憶體的開始結點位址,end表示當前記憶體池的結尾。相當於last到end就是當前記憶體池還未使用的記憶體大小。而它鍊錶的形式就決定了nginx中如果當前記憶體池已滿,便不會像平常一樣擴大該記憶體池,而是重新分配一塊記憶體,鏈到next指標上。但當記憶體池空間不夠卻又未滿時,申請記憶體就回會失敗,failed主要就是記錄失敗的次數,進而從新分配乙個記憶體池。
接著就是表示大塊記憶體的 ngx_pool_large_s
//大塊記憶體的資訊
struct ngx_pool_large_s ;
就是乙個簡單的鍊錶,next指向下一塊記憶體,alloc指向資料。
接下來,通過**來分析具體的方法,更好的理解記憶體池的實現。(僅win32)
首先來看ngx_create_pool
///記憶體池的資料區最大容量
#define max_alloc_from_pool (ngx_pagesize -
1)//建立ngx的記憶體池
ngx_pool_t *
ngx_create_pool(size_t size, ngx_log_t *
log)
//開始初始化資料區
//由於剛剛的開闢記憶體,因此last指標指向資料區的開始。
p->d.last = (u_char *) p + sizeof(ngx_pool_t);
//end指標指向資料區的末尾。
p->d.end = (u_char *) p + size;
p->d.next =
null;
p->d.failed =
0;//當前記憶體池的大小減去記憶體池頭部資訊的大小,得到真正能夠使用的記憶體大小
size = size - sizeof(ngx_pool_t);
//設定max,因為記憶體池的大小不超過一頁(4k),所以記憶體池的最大值也就是size和ngx_max_alloc_from_pool之中較小的。
p->
max= (size < ngx_max_alloc_from_pool) ? size : ngx_max_alloc_from_pool;
//current表示當前記憶體池
p->current = p;
//其他域都置成null
p->chain =
null;
p->large =
null;
p->cleanup =
null;
p->
log=
log;
//返回指標
return p;
}
接下來看看如何從記憶體池中分配一塊記憶體使用。
//考慮記憶體對齊的記憶體池記憶體申請
void* ngx_palloc(size_t size)
//考慮記憶體不對齊的記憶體池記憶體申請
void* ngx_pnalloc(size_t size)
//小塊記憶體申請
void* ngx_alloc_small(size_t size, int i)
//判斷當前記憶體池可用大小與請求的記憶體大小
if ((size_t)(p->d.end - str) >= size)
//否則就繼續遍歷
p = p->d.next;
} while (p);
//表示記憶體池已經滿掉,因此我們需要重新分配乙個記憶體池然後鏈結到當前的data的next上。
return ngx_palloc_block(size);
}
記憶體對齊操作,是乙個記憶體位址取整巨集,畢竟這裡只是對指標進行偏移,last指標的位置需要自己動手將其調整。因為記憶體不對齊的話,會導致cpu i/o的次數增加,效率降低。
#define ngx_align_ptr(p,a) \
(u_char*)(((ngx_uint_t)(p)+((ngx_uint_t)a - 1))& ~((ngx_uint_t)a-1))
ngx_palloc_block的實現主要就是重新分配一塊記憶體池,然後鏈結到當前記憶體池的資料去指標。注意這裡的新記憶體池大小是與其父記憶體池一樣大。
void
* ngx_palloc_block(size_t size)
}//連線到最後乙個記憶體池上
p->d.next = new_p;
//如果current為null,則current就為new_p
pool->current=current?current:new_p;
return str;
}
這樣設定current的原因是因為在ngx_palloc中分配記憶體是從current開始的,將current設定為比較新分配的記憶體會提高效率,而迴圈條件「failed大於4」則在上面說了,同樣為提高效率。
至於大塊記憶體的申請,就是簡單的malloc一塊大塊記憶體鏈結到主記憶體池上。
void
* ngx_alloc_large(size_t size)
if (n++
>
3)//
break;
}//為提高效率,如果3次都沒找到空的,則重新建立乙個
large = (ngx_pool_large_t*)ngx_alloc_small(sizeof(ngx_pool_large_t), 1);
if (large ==
null)
//鏈結資料區指標p到large。這裡可以看到直接插入到large鍊錶的頭的。
large->alloc = p;
large->next =
null;
_pool->large = large;
return p;
}
好了,這些便是記憶體池的記憶體分配, 下面我們來看看記憶體的釋放。這裡我們要知道在nginx中,只有大塊記憶體提供了free介面,可以供我們釋放,而小塊記憶體,是沒有提供這樣的介面的,只有在整個記憶體desrtoy掉時小塊記憶體才會被釋放。
//重置記憶體池
void ngx_reset_pool()
}//重置小塊記憶體區域
for (p = _pool; p;p=p->d.next)
_pool->current = _pool;
_pool->large =
null;
}
//把p指向的記憶體歸還給記憶體池
int ngx_pfree(void
*p)
}return ngx_declined;
}
記憶體池的銷毀
//銷毀記憶體池
void ngx_destroy_pool()
}//最後釋放記憶體池的記憶體池
for (p = _pool, n = _pool->d.next;; p = n, n = n->d.next)
}
nginx記憶體池管理
在src core ngx palloc.h中定義了記憶體池相關的結構體 記憶體相關的 記憶體池 結構體 struct ngx pool cleanup s 大塊資料分配結構體 struct ngx pool large s 記憶體池中資料結構體 typedef struct ngx pool da...
記憶體管理 記憶體管理概述
儲存器的發展方向是高速 大容量和小體積,即儲存器嘗試更高讀寫速度,更大儲存容量,更小物理體積。在計算機中,常見的儲存器有 暫存器,快取,記憶體,硬碟,一般硬碟之類的輔助儲存器又稱外存。在平均讀寫速度上,有 暫存器 快取 記憶體 外存 在單位容量 上,有 外存 記憶體 快取 暫存器 cpu處理器只能直...
《記憶體管理》 記憶體
1.c c 記憶體分布 我們先來看下面的一段 和相關問題 int globalvar 1 static int staticglobalvar 1 void test char char2 abcd char pchar3 abcd int ptr1 int malloc sizeof int 4 ...