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