摘要:源代碼路徑版本主要作用分析提供了一種機制,幫助進行資源管理內存文件。用來標記該使用時分配失敗次數。根據以上思路,可以很容易明白源碼里關于創建鏈表的代碼函數聲明說明輸入要分配的節點大小,返回一個的指針。
源代碼路徑
版本:1.8.0
srccoreNgx_palloc.h srccoreNgx_palloc.c主要作用分析
提供了一種機制,幫助進行資源管理(內存、文件)。可以類比C++中的RAII機制。
以內存管理為例,通常是手工進行malloc/free,這種做法的優點是靈活、高效,缺點是容易出錯,造成內存泄漏以及各種莫名其妙的BUG。
因此,在C/C++中正確的管理內存一直是最棘手的問題。
C++中提供了RAII機制和智能指針來解決這個問題。Nginx采用C語言開發,實現了一套用來管理資源的機制。這就是nginx pool。
數據結構ngx_pool_data_t
typedef struct { u_char *last; u_char *end; ngx_pool_t *next; ngx_uint_t failed; } ngx_pool_data_t;
首先看一個示意圖:
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鏈表就是申請一段內存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); }
今天先到這里,后續再補充或另起一篇寫。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/39133.html
摘要:源碼路徑版本主要作用分析是對通常的這種數據結構重復的造輪子。鏈表使用的內存池。在堆上創建調用函數與的分析類似,調用該函數會自動向申請內存空間。 源碼路徑 版本:1.8.0 srccoreNgx_list.h srccoreNgx_list.c 主要作用分析 ngx_list_t是Nginx對通常的list這種數據結構重復的造輪子。 在本篇中,我們先來分析Nginx是如何造這...
摘要:源文件路徑版本主要作用分析是內部使用的數組型數據結構,與語言內置的數組概念上類似,但是有兩點主要區別使用內存池來管理內存雖然有預設數組大小的概念,但是在數組元素超出預設值大小時,會在內存池中發生重分配。 源文件路徑 版本:1.8.0 srccoreNgx_array.h srccoreNgx_array.c 主要作用分析 ngx_array_t是Nginx內部使用的數組型數據...
摘要:而對于堆內存,通常需要程序員進行管理。我們通常說的內存管理亦是只堆空間內存管理。內存管理整體可以分為個部分,第一部分是常規的內存池,用于進程平時所需的內存管理第二部分是共享內存的管理。將內存塊按照的整數次冪進行劃分最小為最大為。 施洪寶 一. 概述 應用程序的內存可以簡單分為堆內存,棧內存。對于棧內存而言,在函數編譯時,編譯器會插入移動棧當前指針位置的代碼,實現??臻g的自管理。而對于...
閱讀 1331·2019-08-30 15:44
閱讀 1381·2019-08-29 18:42
閱讀 433·2019-08-29 13:59
閱讀 770·2019-08-28 17:58
閱讀 2811·2019-08-26 12:02
閱讀 2414·2019-08-23 18:40
閱讀 2406·2019-08-23 18:13
閱讀 3106·2019-08-23 16:27