nginx原始碼剖析--記憶體池
阿新 • • 發佈:2019-01-18
作者:July、dreamice、阿波、yixiao。
出處:http://blog.csdn.net/v_JULY_v/。
Nginx原始碼剖析之記憶體池
接下來,咱們來看記憶體池的銷燬函式,pool指向需要銷燬的記憶體池
重置記憶體池,將記憶體池恢復到剛分配時的初始化狀態,注意記憶體池分配的初始狀態時,是不包含大塊記憶體的,因此初始狀態需要將使用的大塊記憶體釋放掉,並把記憶體池資料結構的各項指標恢復到初始狀態值。程式碼片段如下:
出處:http://blog.csdn.net/v_JULY_v/。
引言
Nginx(發音同 engine x)是一款輕量級的Web 伺服器/反向代理伺服器及電子郵件(IMAP/POP3)代理伺服器,並在一個BSD-like 協議下發行。由俄羅斯的程式設計師Igor Sysoev所開發,最初供俄國大型的入口網站及搜尋引擎Rambler(俄文:Рамблер)使用。
其特點是佔有記憶體少,併發能力強,事實上nginx的併發能力確實在同類型的網頁伺服器中表現較好,目前中國大陸使用nginx網站使用者有:新浪、網易、 騰訊,另外知名的微網誌Plurk也使用nginx,以及諸多暫不曾得知的玩意兒。讀者可以到此處下載Nginx最新版本的原始碼:http://nginx.org/en/download.html。同時,本文字不想給原始碼太多註釋,因為這不像講解演算法,演算法講解的越通俗易懂越好,而原始碼剖析則不同,緣由在於不同的讀者對同一份原始碼有著不同的理解,或深或淺,所以,更多的是靠讀者自己去思考與領悟。
ok,本文之中有任何疏漏或不正之處,懇請批評指正。謝謝。
Nginx原始碼剖析之記憶體池
1、記憶體池結構
記憶體相關的操作主要在檔案 os/unix/ngx_alloc.{h,c} 和 core/ngx_palloc.{h,c} 中實現,ok,咱們先來看記憶體管理中幾個主要的資料結構:- typedefstruct { //記憶體池的資料結構模組
- u_char *last; //當前記憶體分配結束位置,即下一段可分配記憶體的起始位置
- u_char *end; //記憶體池的結束位置
- ngx_pool_t *next; //連結到下一個記憶體池,記憶體池的很多塊記憶體就是通過該指標連成連結串列的
- ngx_uint_t failed; //記錄記憶體分配不能滿足需求的失敗次數
-
} ngx_pool_data_t; //結構用來維護記憶體池的資料塊,供使用者分配之用。
- struct ngx_pool_t { //記憶體池的管理分配模組
- ngx_pool_data_t d; //記憶體池的資料塊(上面已有描述),設為d
- size_t max; //資料塊大小,小塊記憶體的最大值
- ngx_pool_t *current; //指向當前或本記憶體池
- ngx_chain_t *chain; //該指標掛接一個ngx_chain_t結構
- ngx_pool_large_t *large; //指向大塊記憶體分配,nginx中,大塊記憶體分配直接採用標準系統介面malloc
- ngx_pool_cleanup_t *cleanup; //解構函式,掛載記憶體釋放時需要清理資源的一些必要操作
- ngx_log_t *log; //記憶體分配相關的日誌記錄
- };
- struct ngx_pool_large_t {
- ngx_pool_large_t *next;
- void *alloc;
- };
- typedefstruct {
- ngx_fd_t fd;
- u_char *name;
- ngx_log_t *log;
- } ngx_pool_cleanup_file_t;
- #define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1) //在x86體系結構下,該值一般為4096B,即4K
上述這些資料結構的邏輯結構圖如下(下圖最左上角部分沒有與上文的第一個資料結構內的ngx_uint_t對應起來,特此說明):
1.1、ngx_pool_t的邏輯結構
再看一下用UML繪製的ngx_pool_t的邏輯結構圖:
在下一節,我們將會深入分析記憶體管理的主要函式。
Nginx原始碼剖析之記憶體管理
2、記憶體池操作
2.1、建立記憶體池
- 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);
- //ngx_memalign()函式執行記憶體分配,該函式的實現在src/os/unix/ngx_alloc.c檔案中(假定NGX_HAVE_POSIX_MEMALIGN被定義):
- 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;
- //最大不超過4095B,別忘了上面NGX_MAX_ALLOC_FROM_POOL的定義
- p->current = p;
- p->chain = NULL;
- p->large = NULL;
- p->cleanup = NULL;
- p->log = log;
- return p;
- }
例如,呼叫ngx_create_pool(1024, 0x80d1c4c)後,建立的記憶體池物理結構如下圖:
緊接著,咱們就來分析下上面程式碼中所提到的:ngx_memalign()函式。
- void *
- ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)
- {
- void *p;
- int err;
- err = posix_memalign(&p, alignment, size);
- //該函式分配以alignment為對齊的size位元組的記憶體大小,其中p指向分配的記憶體塊。
- if (err) {
- ngx_log_error(NGX_LOG_EMERG, log, err,
- "posix_memalign(%uz, %uz) failed", alignment, size);
- p = NULL;
- }
- ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,
- "posix_memalign: %p:%uz @%uz", p, size, alignment);
- return p;
- }
- //從這個函式的實現體,我們可以看到p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
- //函式分配以NGX_POOL_ALIGNMENT位元組對齊的size位元組的記憶體,在src/core/ngx_palloc.h檔案中:
- #define NGX_POOL_ALIGNMENT 16
因此,nginx的記憶體池分配,是以16位元組為邊界對齊的。
2.1、銷燬記憶體池接下來,咱們來看記憶體池的銷燬函式,pool指向需要銷燬的記憶體池
- void
- ngx_destroy_pool(ngx_pool_t *pool)
- {
- ngx_pool_t *p, *n;
- ngx_pool_large_t *l;
- ngx_pool_cleanup_t *c;
- 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);
- }
- }
- //前面講到,cleanup指向解構函式,用於執行相關的記憶體池銷燬之前的清理工作,如檔案的關閉等,
- //清理函式是一個handler的函式指標掛載。因此,在這部分,對記憶體池中的解構函式遍歷呼叫。
- 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);
- }
- }
- //這一部分用於清理大塊記憶體,ngx_free實際上就是標準的free函式,
- //即大記憶體塊就是通過malloc和free操作進行管理的。
- #if (NGX_DEBUG)
- /**
- * we could allocate the pool->log from this pool
- * so we can not use this log while the 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;
- }
- }
- //只有debug模式才會執行這個片段的程式碼,主要是log記錄,用以跟蹤函式銷燬時日誌記錄。
- #endif
- for (p = pool, n = pool->d.next; /** void */; p = n, n = n->d.next) {
- ngx_free(p);
- if (n == NULL) {
- break;
- }
- }
- }
- //該片段徹底銷燬記憶體池本身。
2.3、重置記憶體池
void ngx_reset_pool(ngx_pool_t *pool)重置記憶體池,將記憶體池恢復到剛分配時的初始化狀態,注意記憶體池分配的初始狀態時,是不包含大塊記憶體的,因此初始狀態需要將使用的大塊記憶體釋放掉,並把記憶體池資料結構的各項指標恢復到初始狀態值。程式碼片段如下:
- 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);
- }
- }
- //上述片段主要用於清理使用到的大塊記憶體。
- pool->large = NULL;
- for (p = pool; p; p = p->d.next) {
- p->d.last = (u_char *) p + sizeof(ngx_pool_t);