摘要:關(guān)于是自身實(shí)現(xiàn)的一個內(nèi)存池模塊,其遍及整個的源碼之中,也是能簡潔高效處理各個請求的基礎(chǔ)所在。它本身是一個記錄表,其中記錄了整個內(nèi)內(nèi)存池的內(nèi)存分配信息鏈的頭指針。
關(guān)于
palloc是nginx自身實(shí)現(xiàn)的一個內(nèi)存池模塊,其遍及整個nginx的源碼之中,也是nginx能簡潔高效處理各個請求的基礎(chǔ)所在。本文先從ngx_alloc和ngx_palloc2個文件來解讀內(nèi)存模塊。
ngx_alloc文件整個ngx_alloc包含了3個函數(shù):ngx_alloc、ngx_calloc和ngx_memalign。
其中ngx_alloc和ngx_calloc方法都是利用malloc方法來分配內(nèi)存,不同的是ngx_calloc方法會在分配后進(jìn)行初始化工作。
而ngx_memalign方法,則是利用memalign或posix_memalign方法申請一個內(nèi)存對齊的內(nèi)存塊。
ngx_palloc模塊結(jié)構(gòu)體內(nèi)存對齊的用處首先是可以提高cpu效率,因?yàn)椴粚R會導(dǎo)致cpu訪問內(nèi)存時候需要拆分內(nèi)存塊;第二是方便平臺的移植。
上節(jié)的ngx_alloc文件是對c語言內(nèi)存的封裝,此后的內(nèi)存分配都是通過調(diào)取其中的三個方法進(jìn)行的。那么我們先來了解一下ngx_palloc包含的結(jié)構(gòu)體。
ngx_pool_s結(jié)構(gòu)體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; };
ngx_pool_s結(jié)構(gòu)體是整個內(nèi)存池的核心結(jié)構(gòu)體。它本身是一個記錄表,其中記錄了整個內(nèi)內(nèi)存池的內(nèi)存分配信息鏈的頭指針。其中主要的屬性分別是d、large和cleanup三個屬性,這也是我們接下來要了解的三個結(jié)構(gòu)體的指針。
ngx_pool_data_t結(jié)構(gòu)體typedef struct { u_char *last; u_char *end; ngx_pool_t *next; ngx_uint_t failed; } ngx_pool_data_t;
ngx_pool_data_t結(jié)構(gòu)體其實(shí)就像是ngx_pool_s結(jié)構(gòu)體的一個詳細(xì)描述,其中描述了一個內(nèi)存池的信息,包括當(dāng)前分配完的內(nèi)存地址、內(nèi)存池最后的內(nèi)存地址、下一個內(nèi)存池指針以及分配內(nèi)存失敗次數(shù)。
ngx_pool_large_s結(jié)構(gòu)體struct ngx_pool_large_s { ngx_pool_large_t *next; void *alloc; };
這個結(jié)構(gòu)體就比較簡單,就算一個鏈表,并包含一個指針指向當(dāng)前分配的內(nèi)存塊。
ngx_pool_cleanup_s結(jié)構(gòu)體struct ngx_pool_cleanup_s { ngx_pool_cleanup_pt handler; void *data; ngx_pool_cleanup_t *next; };
ngx_pool_cleanup_s結(jié)構(gòu)體的功能主要是用來在銷毀內(nèi)存池時,需要處理一下其他的操作來保證內(nèi)存的正常銷毀,避免內(nèi)存的泄露。因此,在銷毀內(nèi)存期間,會觸發(fā)這個ngx_pool_cleanup_s的鏈表,并以此執(zhí)行銷毀函數(shù)。
ngx_pool_cleanup_file_t結(jié)構(gòu)體typedef struct { ngx_fd_t fd; u_char *name; ngx_log_t *log; } ngx_pool_cleanup_file_t;
這個結(jié)構(gòu)體,主要用途就是為了在銷毀內(nèi)存塊的時候,能對文件描述符進(jìn)行關(guān)閉等操作。(感覺是這樣)
ngx_palloc模塊函數(shù) ngx_create_pool方法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); 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; p->current = p; p->chain = NULL; p->large = NULL; p->cleanup = NULL; p->log = log; return p; }
這個方法主要是利用ngx_memalign方法來分配內(nèi)存塊,然后計算出d.last和d.end的2個屬性,其他屬性都比較容易理解。
ngx_palloc方法以及ngx_pnalloc方法void * ngx_palloc(ngx_pool_t *pool, size_t size) { #if !(NGX_DEBUG_PALLOC) if (size <= pool->max) { return ngx_palloc_small(pool, size, 1); } #endif return ngx_palloc_large(pool, size); }
該函數(shù)理解比較簡單,就算判斷內(nèi)存塊大小是否大于最大的內(nèi)存塊,若大于則使用大塊內(nèi)存的分配。
ngx_palloc_small方法static ngx_inline void * ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align) { u_char *m; ngx_pool_t *p; p = pool->current; do { m = p->d.last; if (align) { m = ngx_align_ptr(m, 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); }
在分配小塊內(nèi)存時,就算不斷的尋找是否存在符合條件的內(nèi)存大小,若存在,則將內(nèi)存塊地址返回,并將d.last往后移動分配的內(nèi)存大小,即完成了內(nèi)存分配。若不存在,則利用ngx_palloc_block方法去生成一個新的內(nèi)存塊。
ngx_palloc_block方法static void * ngx_palloc_block(ngx_pool_t *pool, size_t size) { u_char *m; size_t psize; ngx_pool_t *p, *new; psize = (size_t) (pool->d.end - (u_char *) pool); m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log); if (m == NULL) { return NULL; } new = (ngx_pool_t *) m; new->d.end = m + psize; new->d.next = NULL; new->d.failed = 0; m += sizeof(ngx_pool_data_t); m = ngx_align_ptr(m, NGX_ALIGNMENT); new->d.last = m + size; 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; }
該函數(shù)其實(shí)用途在于重新生成一個新的內(nèi)存池,同時內(nèi)存池的大小和最初的內(nèi)存池是相同大小。關(guān)鍵在于,他會對失敗大于4次的內(nèi)存池的當(dāng)前指針進(jìn)行移動,這樣可以提高之后的內(nèi)存查找的效率。
ngx_palloc_large方法static void * ngx_palloc_large(ngx_pool_t *pool, size_t size) { void *p; ngx_uint_t n; ngx_pool_large_t *large; p = ngx_alloc(size, pool->log); if (p == NULL) { return NULL; } n = 0; for (large = pool->large; large; large = large->next) { if (large->alloc == NULL) { large->alloc = p; return p; } if (n++ > 3) { break; } } large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1); if (large == NULL) { ngx_free(p); return NULL; } large->alloc = p; large->next = pool->large; pool->large = large; return p; }
nginx內(nèi)存池有趣的地方就在于,他們直接可能會互相調(diào)用來實(shí)現(xiàn)自己的功能,例如當(dāng)前的方法,首先它回去直接申請一個需要的內(nèi)存塊,之后它需要去查找ngx_pool_large_t的鏈表,看看有沒有某個ngx_pool_large_t的alloc是為空的,這樣就可以將分配好的地址掛載上去。
若不存在,那么就利用small方法申請一個ngx_pool_large_t的節(jié)點(diǎn),然后將其加入ngx_pool_large_t的鏈表中。
void * ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment) { void *p; ngx_pool_large_t *large; p = ngx_memalign(alignment, size, pool->log); if (p == NULL) { return NULL; } large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1); if (large == NULL) { ngx_free(p); return NULL; } large->alloc = p; large->next = pool->large; pool->large = large; return p; }
該方法就是ngx_palloc_large簡單暴力版,直接申請ngx_pool_large_t并加入鏈表中。
ngx_destroy_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); } } #if (NGX_DEBUG) ... ... #endif for (l = pool->large; l; l = l->next) { if (l->alloc) { ngx_free(l->alloc); } } for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { ngx_free(p); if (n == NULL) { break; } } }
ngx_destroy_pool方法的執(zhí)行流程主要如下:先進(jìn)行cleanup操作,觸發(fā)銷毀方法、再進(jìn)行大塊內(nèi)存的銷毀、最后銷毀銷毀內(nèi)存。銷毀方法都是使用ngx_free,其實(shí)就算free方法。
ngx_pool_cleanup_add方法ngx_pool_cleanup_t * ngx_pool_cleanup_add(ngx_pool_t *p, size_t size) { ngx_pool_cleanup_t *c; c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t)); if (c == NULL) { return NULL; } if (size) { c->data = ngx_palloc(p, size); if (c->data == NULL) { return NULL; } } else { c->data = NULL; } c->handler = NULL; c->next = p->cleanup; p->cleanup = c; ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c); return c; }
該方法主要是為內(nèi)存池添加一個銷毀的接口對象,先進(jìn)行分配內(nèi)存塊,之后再在該內(nèi)存上初始化變量,變量類似ngx_pool_cleanup_file_t,然后設(shè)置handle屬性,用于以后內(nèi)存池銷毀。
總結(jié)nginx的內(nèi)存池功能相對stl的內(nèi)存池更好理解,也許是代碼風(fēng)格問題導(dǎo)致閱讀難度的增加。不過學(xué)習(xí)了nginx的內(nèi)存分配后,就可以開始其他的模塊的閱讀。
相關(guān)鏈接為什么要進(jìn)行內(nèi)存對齊以及對齊規(guī)則
nginx源碼分析—內(nèi)存池結(jié)構(gòu)ngx_pool_t及內(nèi)存管理
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/39649.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是如何造這...
摘要:源代碼路徑版本主要作用分析提供了一種機(jī)制,幫助進(jìn)行資源管理內(nèi)存文件。用來標(biāo)記該使用時分配失敗次數(shù)。根據(jù)以上思路,可以很容易明白源碼里關(guān)于創(chuàng)建鏈表的代碼函數(shù)聲明說明輸入要分配的節(jié)點(diǎn)大小,返回一個的指針。 源代碼路徑 版本:1.8.0 srccoreNgx_palloc.h srccoreNgx_palloc.c 主要作用分析 提供了一種機(jī)制,幫助進(jìn)行資源管理(內(nèi)存、文件)。可以...
摘要:而對于堆內(nèi)存,通常需要程序員進(jìn)行管理。我們通常說的內(nèi)存管理亦是只堆空間內(nèi)存管理。內(nèi)存管理整體可以分為個部分,第一部分是常規(guī)的內(nèi)存池,用于進(jìn)程平時所需的內(nèi)存管理第二部分是共享內(nèi)存的管理。將內(nèi)存塊按照的整數(shù)次冪進(jìn)行劃分最小為最大為。 施洪寶 一. 概述 應(yīng)用程序的內(nèi)存可以簡單分為堆內(nèi)存,棧內(nèi)存。對于棧內(nèi)存而言,在函數(shù)編譯時,編譯器會插入移動棧當(dāng)前指針位置的代碼,實(shí)現(xiàn)棧空間的自管理。而對于...
閱讀 1058·2021-11-12 10:34
閱讀 985·2021-09-30 09:56
閱讀 668·2019-08-30 15:54
閱讀 2602·2019-08-30 11:14
閱讀 1465·2019-08-29 16:44
閱讀 3203·2019-08-29 16:35
閱讀 2489·2019-08-29 16:22
閱讀 2441·2019-08-29 15:39