版本:1.8.0nginx
src\core\Ngx_palloc.h src\core\Ngx_palloc.c
提供了一種機制,幫助進行資源管理(內存、文件)。能夠類比C++
中的RAII
機制。數據結構
之內存管理爲例,一般是手工進行malloc/free
,這種作法的優勢是靈活、高效,缺點是容易出錯,形成內存泄漏以及各類莫名其妙的BUG。函數
所以,在C/C++
中正確的管理內存一直是最棘手的問題。ui
C++
中提供了RAII
機制和智能指針來解決這個問題。Nginx
採用C
語言開發,實現了一套用來管理資源的機制。這就是nginx pool
。spa
ngx_pool_data_t指針
typedef struct { u_char *last; u_char *end; ngx_pool_t *next; ngx_uint_t failed; } ngx_pool_data_t;
首先看一個示意圖:code
last
指針表示ngx_pool_data_t
所管理的內存block
中已使用的內存的末尾地址,這也是下次分配的起始地址。接口
end
指針表示ngx_pool_data_t
所管理的內存block
的尾地址,該指針在分配此block
時肯定,用來標記last
的邊界值。圖片
next
用來指向下一個ngx_pool_data_t
的內存block
地址,用於造成block
鏈表。failed
用來標記該block
使用時分配失敗次數。內存
圍繞核心數據結構ngx_pool_data_t
, Nginx
是如何管理和使用的呢?
本篇主要從如下幾個方面說明:
1) 如何初始化ngx_pool_data_t
鏈表;
2) 如何向鏈表增長ngx_pool_data_t
節點。
3) 如何回收/銷燬ngx_pool_data_t
節點及鏈表
ngx_pool_data_t
鏈表基本思路:
ngx_pool_data_t
鏈表就是申請一段內存block
,而後將該block
指針void* p
轉成ngx_pool_data_t*
類型的指針,這樣,就能夠利用ngx_pool_data_t*
指針來管理這段block
,而這個block
就是ngx_pool_data_t
鏈表的節點。block
還須要初始化ngx_pool_data_t
的成員變量last
、end
、next
、failed
。這樣,經過ngx_pool_data_t
指針,能夠獲取管理ngx_pool_data_t
鏈表節點的能力 ngx_pool_data_t
鏈表的能力,那麼如何作呢?經過在內存block
的起始部分添加last
、end
、next
、failed
等信息能夠管理一段內存。相應地,經過在ngx_pool_data_t
鏈表第一個節點添加管理鏈表的信息,就能夠管理整個鏈表。同時,因爲鏈表第一個節點只是一個特殊的節點因此,負責管理節點的結構體ngx_pool_data_t
應該是的負責管理鏈表結構體節點的子集
在Nginx
源碼中這個管理鏈表的結構體就是:ngx_pool_s
,其結構體定義爲:
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; };
其中,d
是ngx_pool_data_t
用來管理節點自己,而其餘變量則用來管理鏈表。
current
用來指示鏈表中當前正在使用的節點;chain
用來在節點上掛接filter
鏈表;cleanup
用來註冊資源回收handler等;large
是與節點大內存
、小內存
有關,這裏暫不分析。經過以上分析,可知,ngx_pool_s
是管理整個ngx_pool_data_t
鏈表,同時也可以管理所在的鏈表節點。
在Nginx
源碼裏,申請的block
指針爲void*
統一轉成ngx_pool_s*
,這樣,在鏈表第一個節點,經過ngx_pool_s*
管理整個鏈表及節點自己,在其餘節點,經過ngx_pool_s*
管理節點自己。
根據以上思路,能夠很容易明白Nginx
源碼裏關於建立ngx_pool_data_t
鏈表的代碼
函數聲明: Ngx_palloc.h
ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
說明:
輸入要分配的ngx_pool_t
節點block
大小size
,返回一個ngx_pool_t*
的指針。該指針既用來管理鏈表,也用來管理節點block
自己。
所以,後續鏈表的插入,刪除都是經過操做該指針來完成,而使用鏈表第一個節點的block
也是經過該指針來完成。
函數定義: Ngx_palloc.c
ngx_pool_t * ngx_create_pool(size_t size, ngx_log_t *log) { ngx_pool_t *p; // 申請內存block,爲提升效率,進行了對齊 // 轉成ngx_pool_t*指針 p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log); if (p == NULL) { return NULL; } // 初始化ngx_pool_data_t用來管理節點block自己 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; // 爲提升效率能使用的block最大值爲NGX_MAX_ALLOC_FROM_POOL // 輸入size最小值爲sizeof(ngx_pool_t),輸入小於該值會形成nginx崩潰 size = size - sizeof(ngx_pool_t); p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL; // 初始時,鏈表current指向自身 p->current = p; p->chain = NULL; p->large = NULL; p->cleanup = NULL; p->log = log; return p; }
ngx_pool_data_t
節點基本思路:
上一小節中關於如何初始化ngx_pool_data_t
鏈表的思路,能夠基本套用到增長ngx_pool_data_t
節點中來,只是在結構體ngx_pool_t
成員變量初始化上有所區別。
直接上源碼:
static void * ngx_palloc_block(ngx_pool_t *pool, size_t size) { // 輸入變量pool用來表示整個鏈表 u_char *m; size_t psize; ngx_pool_t *p, *new; // 計算鏈表第一個節點的block大小 psize = (size_t) (pool->d.end - (u_char *) pool); // 申請與鏈表第一個節點大小相同的內存block m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log); if (m == NULL) { return NULL; } // 轉爲ngx_pool_t*類型 new = (ngx_pool_t *) m; // 初始化節點管理結構體ngx_pool_data_t new->d.end = m + psize; new->d.next = NULL; new->d.failed = 0; // 爲了效率,對ngx_pool_data_t的last變量進行對齊操做 m += sizeof(ngx_pool_data_t); m = ngx_align_ptr(m, NGX_ALIGNMENT); new->d.last = m + size; // 將第一個鏈表節點用於操做鏈表的pool指針的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; }
固然,在使用時,Nginx
並非直接使用ngx_palloc_block
來操做鏈表,而是使用ngx_palloc
緣由在於,使用ngx_create_pool
和ngx_palloc_block
構造鏈表以後,咱們獲得的是多個連續的內存塊組成的列表,這些內存塊大小相同,其大小爲調用ngx_create_pool
傳入的size
值或NGX_MAX_ALLOC_FROM_POOL
。
而在實際使用時malloc
的大小不必定等於這些內存塊的大小,因此,須要提供一個接口,可以從ngx_pool_t
鏈表中獲取須要大小的內存。
這個函數就是ngx_palloc
基本思路:
若是要獲取的內存大小大於ngx_pool_t
節點的大小,那麼屬於建立大內存,調用ngx_palloc_large(pool, size)
。這裏咱們暫不分析大內存的問題。
若是要獲取的內存小於ngx_pool_t
節點的大小,那麼嘗試從當前節點分配。
這裏分兩種狀況:
P.S 這裏的分配就是返回指針
函數聲明:
void *ngx_palloc(ngx_pool_t *pool, size_t size);
函數定義:
void * ngx_palloc(ngx_pool_t *pool, size_t size) { u_char *m; ngx_pool_t *p; // 獲取內存小於鏈表節點內存的狀況 if (size <= pool->max) { p = pool->current; // 嘗試從當前節點分配內存 do { m = ngx_align_ptr(p->d.last, 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); } // 獲取內存小於鏈表節點內存的狀況 return ngx_palloc_large(pool, size); }
今天先到這裏,後續再補充或另起一篇寫。