源码 C Nginx Nginx 内存池 Johnny 2024-01-11 2024-01-11 Nginx 内存池 内存池结构 内存池中主要涉及结构体:
创建内存池 程序首先会创建一个内存池,之后程序内需要使用内存时,会从内存池中分配内存。 创建内存池的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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; }
创建之后的内存如下:
last指向可分配内存的首地址
end指向当前内存池的结束地址
next指向内存池下一个内存块的地址
failed记录内存分配失败次数
max: 最大可分配内存
current:分配地址时可使用的内存块
large:指向大块内存的管理信息(在内存池中)
cleanup: 清理函数链
申请内存 Nginx申请内存通过三个API:ngx_palloc、 ngx_pcalloc、 ngx_pnalloc
ngx_palloc:获取内存后并不进行初始化操作
ngx_pcalloc: 对内存进行初始化
ngx_palloc的首地址是对齐的,而ngx_pnalloc没有考虑内存对齐
1 2 3 4 5 6 7 8 9 10 11 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); }
如果申请的内存小于等于内存池的最大可用空间,则会分配小块内存,否则分配大块内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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); }
如果该内存池没有这么大的内存,就会重新建立一个内存块进行分配,同时将前一个内存池的next指针指向新建的内存块上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 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; }
大块内存分配。 管理大块内存的数据结构所用的空间是从内存池中申请的,而申请的大块内存是从堆上分配的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 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; }
释放内存 对于在内存池中的小块内存不需要释放,会在内存池释放之后释放。 通过ngx_pfree进行大块内存释放。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ngx_int_t ngx_pfree (ngx_pool_t *pool, void *p) { ngx_pool_large_t *l; for (l = pool->large; l; l = l->next) { if (p == l->alloc) { ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log , 0 , "free: %p" , l->alloc); ngx_free(l->alloc); l->alloc = NULL ; return NGX_OK; } } return NGX_DECLINED; }
先遍历大块内存表找到需要释放的大块内存,然后释放当前的内存块。这里释放的是内存块,由于存储内存块的链表节点属于内存池的小块内存,所以肯定不会释放掉。
释放内存池 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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); } } for (l = pool->large; l; l = l->next) { if (l->alloc) { ngx_free(l->alloc); } } for (p = pool, n = pool->d.next; ; p = n, n = n->d.next) { ngx_free(p); if (n == NULL ) { break ; } } }