記憶體池的使用給Nginx帶來了很多好處,比如記憶體使用量的便利,邏輯代碼的簡化以及程式效能的提升。
幾個關鍵知識點羅列如下:
(1)函數ngx_palloc()嘗試從記憶體中分配size大小的記憶體時,分兩種情況,一種是size大小小於pool->max,稱為小塊記憶體配置,若當前記憶體池節點小於size,則申請一個新的等同大小的記憶體池節點,然後從這個新記憶體池節點分配出size大小的記憶體空間。若size 大於pool->max時,即分配大塊記憶體,此時調用的函數直接向作業系統申請大塊記憶體。
(2)小塊記憶體的申請是插入在鏈表的尾節點,而新的大塊記憶體則是插入在鏈表前面。
(3)Nginx僅提供對大塊記憶體的釋放,沒有提供對小塊記憶體的釋放,意味著從記憶體池分配出去的記憶體不會再回收到記憶體池裡來,而只有在銷毀整個記憶體池時,這些記憶體才會回收到系統記憶體裡。
(4)ngx_pool_t中的current欄位:這個欄位記錄了後序從該記憶體池分配記憶體的起始節點,Nginx規定當一個記憶體節點分配總失敗次數大於等於6次時,current則指向下一個記憶體節點。
(5)為什麼要將pool->max欄位的最大值限制在一頁記憶體,這也是小塊記憶體與大塊記憶體的臨界,原因在於只有當分配的空間小於一頁時才有緩衝的必要,,否則的話還不如直接利用系統介面malloc()向作業系統申請。
各個結構體的定義:
//大塊記憶體管理結構 struct ngx_pool_large_s { ngx_pool_large_t *next; //串連下一個大記憶體管理 void *alloc; //申請的大記憶體位址 }; //記憶體池中資料管理 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; //存放資料的可用記憶體大小,最大為1頁 ngx_pool_t *current; //指向分配記憶體的記憶體池 ngx_chain_t *chain; ngx_pool_large_t *large; //串連大記憶體管理結構 ngx_pool_cleanup_t *cleanup; //清理對象頭 ngx_log_t *log; }; 記憶體池的初始化:
//建立一個size的記憶體池 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); //以對齊的方式來申請size位元組記憶體 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; //初始時,下一個可用記憶體為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; } 記憶體池的銷毀與重設:
//銷毀記憶體池 void ngx_destroy_pool(ngx_pool_t *pool) { ngx_pool_t *p, *n; ngx_pool_large_t *l; ngx_pool_cleanup_t *c; //運行清理對象的handler 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); } } //釋放大記憶體 for (l = pool->large; l; l = l->next) { ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc); if (l->alloc) { ngx_free(l->alloc); //使用free釋放malloc申請的記憶體 } } #if (NGX_DEBUG) /* * we could allocate the pool->log from this pool * so we cannot use this log while free()ing the pool */ 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 //釋放每一個申請的記憶體池對象ngx_pool_t for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { ngx_free(p); if (n == NULL) { break; } } } //重設記憶體池 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); } } //記憶體池對象,僅僅改變last的指標位置 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; } 分配記憶體:
//分配記憶體(地址對齊) void * ngx_palloc(ngx_pool_t *pool, size_t size) { u_char *m; ngx_pool_t *p; if (size <= pool->max) { //小記憶體申請時,以size為標準 p = pool->current; do { m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT); //首先將d.last地址對齊 if ((size_t) (p->d.end - m) >= size) { //可用的記憶體大於要申請的記憶體 p->d.last = m + size; //直接更新d.last return m; //直接返回 } p = p->d.next; //否則找下一個可用的記憶體池對象 } while (p); //沒有找到,則要申請新的記憶體池對象 return ngx_palloc_block(pool, size); } return ngx_palloc_large(pool, size); //大記憶體申請處理 } //分配記憶體(地址可以不對齊) void * ngx_pnalloc(ngx_pool_t *pool, size_t size) { u_char *m; ngx_pool_t *p; if (size <= pool->max) { //小記憶體 p = pool->current; do { m = p->d.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); //大記憶體 } 小塊記憶體配置:
//申請新的記憶體池對象 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); //只有一個ngx_pool_data_t,節省了ngx_pool_t的其餘開銷 m = ngx_align_ptr(m, NGX_ALIGNMENT); new->d.last = m + size; //可用的記憶體的起始地址 //如果進行中的申請記憶體的失敗的次數已經有5次了,第6次,current將會指向新的記憶體池對象 for (p = pool->current; p->d.next; p = p->d.next) { if (p->d.failed++ > 4) { pool->current = p->d.next; } } p->d.next = new; //串連剛剛申請的記憶體池對象 return m; } 大塊記憶體配置
//大記憶體申請處理 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); //直接malloc申請記憶體 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) { //但是只找4次,第5次直接break,建立大記憶體的管理結構 break; } } large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); //從記憶體池對象申請記憶體 if (large == NULL) { ngx_free(p); return NULL; } large->alloc = p; //指向申請的大記憶體 //插入large的頭 large->next = pool->large; pool->large = large; return p; } 直接分配記憶體:
//不管記憶體大小多大,向作業系統申請記憶體 void * ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment) { void *p; ngx_pool_large_t *large; p = ngx_memalign(alignment, size, pool->log); //申請的記憶體 if (p == NULL) { return NULL; } large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); //申請一個大記憶體管理結構 if (large == NULL) { ngx_free(p); return NULL; } //放入到記憶體池ngx_pool_t中管理 large->alloc = p; //指向申請的記憶體 //插入到頭部 large->next = pool->large; pool->large = large; return p; } 釋放記憶體:
//釋放記憶體 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; }
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
以上就介紹了Nginx進階資料結構源碼分析(四)-----記憶體池,包括了方面的內容,希望對PHP教程有興趣的朋友有所協助。