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