摘要:源文件路徑版本主要作用分析是內部使用的數組型數據結構,與語言內置的數組概念上類似,但是有兩點主要區別使用內存池來管理內存雖然有預設數組大小的概念,但是在數組元素超出預設值大小時,會在內存池中發生重分配。
源文件路徑
版本:1.8.0
srccoreNgx_array.h srccoreNgx_array.c主要作用分析
ngx_array_t是Nginx內部使用的數組型數據結構,與C語言內置的數組概念上類似,但是有兩點主要區別:
1)ngx_array_t使用ngx_pool_t內存池來管理內存;
2)ngx_array_t雖然有預設數組大小的概念,但是在數組元素超出預設值大小時,會在ngx_pool_t內存池中發生重分配。
但是需要指出,雖然ngx_array_t支持超出數組預設值,但是在內存重分配之后并不會重新利用原來的內存,會造成部分內存浪費。
數據結構ngx_array_t
typedef struct { void *elts; ngx_uint_t nelts; size_t size; ngx_uint_t nalloc; ngx_pool_t *pool; } ngx_array_t;
從內存上來看,array是一塊連續的內存區域。因此,作為描述數組的結構體需要:
描述起始地址及描述結束地址,此外需要描繪數組元素的大小以便于索引數組的每個元素,以及描述內存區域已使用的大小
這樣,ngx_array_t的每個成員變量就很容易理解了:
elts用來描述數組使用的內存塊的起始地址;
size用來描述數組元素的大??;
nalloc用來描述內存塊最多能容納的數組元素個數,因此,內存塊的結束地址= elts+nalloc*size
nelts用來描述當前內存塊已存在的元素個數;
pool表示ngx_array_t使用的內存所在的內存池。
ngx_array_t的管理和使用ngx_array_t的使用可以從以下幾個方面來分析:
1)ngx_array_t的創建;
2)如何向ngx_array_t添加元素;
3)如何銷毀ngx_array_t;
因為ngx_array_t使用elts指針來指向ngx_array_t實際使用的內存塊,所以,ngx_array_t的創建分成兩部分:
1.ngx_array_t結構體本身的創建;
2.ngx_array_t所管理的內存的創建;
在堆上創建ngx_array_t結構體本身,Nginx提供了函數:
ngx_array_t *ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);
其定義如下:
ngx_array_t * ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size) { ngx_array_t *a; a = ngx_palloc(p, sizeof(ngx_array_t)); if (a == NULL) { return NULL; } if (ngx_array_init(a, p, n, size) != NGX_OK) { return NULL; } return a; }
從源代碼可知:在堆上創建ngx_array_t結構體時,同時也創建了其所管理的內存。
ngx_array_t結構體本身的創建兩種方式:在堆上創建、在棧上創建
在堆上創建,需要使用ngx_pool_t來管理內存。
= 在棧上創建,直接創建ngx_array_t局部變量即可。
向ngx_pool_t申請。
ngx_array_t所管理內存的創建,Nginx提供了函數:
static ngx_inline ngx_int_t ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size) { /* * set "array->nelts" before "array->elts", otherwise MSVC thinks * that "array->nelts" may be used without having been initialized */ array->nelts = 0; array->size = size; array->nalloc = n; array->pool = pool; array->elts = ngx_palloc(pool, n * size); if (array->elts == NULL) { return NGX_ERROR; } return NGX_OK; }
這個函數很容易看明白。
輸入在堆上或棧上創建的ngx_array_t結構體、申請內存使用的ngx_pool_t內存池、申請的數組元素數目、元素的大小。
函數將elts指向申請的內存空間首地址。
ngx_array_t添加元素向ngx_array_t添加元素就是對內存進行操作。只需要提供elts + nelts * size指針,向其寫入size大小的數據即為添加元素。
函數聲明:
void *ngx_array_push(ngx_array_t *a);
函數定義:
void * ngx_array_push(ngx_array_t *a) { void *elt, *new; size_t size; ngx_pool_t *p; // 數組元素超過預設值時發生內存重新分配 if (a->nelts == a->nalloc) { /* the array is full */ size = a->size * a->nalloc; p = a->pool; if ((u_char *) a->elts + size == p->d.last && p->d.last + a->size <= p->d.end) { /* * the array allocation is the last in the pool * and there is space for new allocation */ p->d.last += a->size; a->nalloc++; } else { /* allocate a new array */ new = ngx_palloc(p, 2 * size); if (new == NULL) { return NULL; } // 直接將原來的內容拷貝到新內存塊中,原來的內存沒有重新利用 ngx_memcpy(new, a->elts, size); a->elts = new; a->nalloc *= 2; } } elt = (u_char *) a->elts + a->size * a->nelts; a->nelts++; return elt; }
調用ngx_array_push獲取分配給插入元素的內存地址。如果元素個數超過預設值,發生重分配內存。原來的內存沒有處理,因此會發生浪費。
另外Nginx還提供了ngx_array_push_n這個函數來處理插入n個元素的情況。
可知,ngx_array_push是ngx_array_push_n中n=1是的特殊情況。他們的代碼也基本相同。C語言不支持默認值參數,否則,這兩個函數可以合成一個。
ngx_array_t銷毀根據ngx_array_t創建的分析,可知,ngx_array_t的銷毀其實就是不去使用ngx_array_t。
因為,如果在堆上創建ngx_array_t,那么有ngx_pool_t負責管理內存,如果在棧上創建ngx_array_t則變量自動銷毀。
而ngx_array_t所管理的內存有ngx_pool_t來負責管理。所以,只要不再使用ngx_array_t或者將ngx_array_t指針置空,則ngx_array_t銷毀。
但是Nginx提供了一個用來destory的函數,我們來看看它做了些什么。
void ngx_array_destroy(ngx_array_t *a) { ngx_pool_t *p; p = a->pool; // 釋放ngx_array_t所管理的內存 if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) { p->d.last -= a->size * a->nalloc; } // 釋放在堆中的ngx_array_t結構體本身 if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) { p->d.last = (u_char *) a; } }
這個函數可能會發生兩種重新回收利用內存的情況:
當ngx_array_t所管理的內存正好是ngx_pool_t最近一次分配的內存。
當堆中的ngx_array_t結構體變量本身正好是ngx_pool_t最近一次分配的內存。
所以,在使用完ngx_array_t之后,最好調用該函數,雖然它可能什么都會做,但是也可能進行內存池內存的重新利用,減少內存浪費。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/39162.html
摘要:源碼路徑版本主要作用分析是對通常的這種數據結構重復的造輪子。鏈表使用的內存池。在堆上創建調用函數與的分析類似,調用該函數會自動向申請內存空間。 源碼路徑 版本:1.8.0 srccoreNgx_list.h srccoreNgx_list.c 主要作用分析 ngx_list_t是Nginx對通常的list這種數據結構重復的造輪子。 在本篇中,我們先來分析Nginx是如何造這...
摘要:相關系列前面分析了數組,現在看一下隊列和哈希表的實現。隊列是一個雙向鏈表,實現了一個隊列的操作邏輯。它們都將鏈表節點塞入數據結構。對于常用的解決沖突的方法有線性探測二次探測和開鏈法等。 相關系列:http://www.codefrom.com/p/nginx 前面分析了ngx_array_t數組,現在看一下ngx_queue隊列和ngx_hash哈希表的實現。 ngx_qu...
摘要:找到這個模塊的指令后,則會調用這個指令的解析回調函數即結構體的第三個參數來進行處理。調用他們上面提到的中的回調函數來申請和初始化對應模塊的配置結構體。需要注意的是,即時當前是直接在塊級別,這三個回調函數都會被調用。拒絕暴力枚舉式編寫配置文件 原博:https://blog.coordinate35.cn/... 熱身 首先來看下這幾個小例子: 第一個例子: server { l...
閱讀 1072·2021-11-24 10:27
閱讀 3345·2021-11-18 10:02
閱讀 2405·2021-11-16 11:45
閱讀 3168·2021-11-15 18:10
閱讀 837·2021-09-22 15:23
閱讀 1538·2019-08-30 15:53
閱讀 3028·2019-08-30 13:20
閱讀 1675·2019-08-30 12:53