The use of memory pool brings many benefits to Nginx, such as convenience of memory usage, simplification of logic code and improvement of program performance.
A few key knowledge points are listed below:
(1) When the function ngx_palloc() tries to allocate memory of size size from memory, there are two situations. One is that size size is smaller than pool->max , called small memory allocation, if the current memory pool node is smaller than size, apply for a new memory pool node of the same size, and then allocate memory space of size size from this new memory pool node. If size is greater than pool->max, a large block of memory is allocated, and the function called at this time directly applies for a large block of memory from the operating system.
(2) The application for a small block of memory is inserted at the tail node of the linked list, while the new large block of memory is inserted at the front of the linked list.
(3) Nginx only provides the release of large blocks of memory and does not provide the release of small blocks of memory, which means that the memory allocated from the memory pool will not be recycled back to the memory pool, but only after the entire When the memory pool is opened, the memory will be recycled into the system memory.
(4) The current field in ngx_pool_t: This field records the starting node from which memory is allocated from the memory pool. Nginx stipulates that when the total number of allocation failures for a memory node is greater than or equal to 6 times, current points downward. A memory node.
(5) Why is it necessary to limit the maximum value of the pool->max field to one page of memory? This is also the critical point between small memory and large memory. The reason is that it is only available when the allocated space is less than one page. Caching is necessary, otherwise it is better to directly use the system interface malloc() to apply to the operating system.
Definition of each structure:
//大块内存管理结构 struct ngx_pool_large_s { ngx_pool_large_t *next; //连接下一个大内存管理 void *alloc; //申请的大内存地址 }; //内存池中数据管理 typedef struct { u_char *last; //可用内存的起始地址 u_char *end; //可用内存的末尾地址 ngx_pool_t *next; //指向下一个内存池节点 ngx_uint_t failed; //申请时,失败的次数 } ngx_pool_data_t; //内存池 struct ngx_pool_s { ngx_pool_data_t d; //存放数据 size_t max; //存放数据的可用内存大小,最大为1页 ngx_pool_t *current; //指向分配内存的内存池 ngx_chain_t *chain; ngx_pool_large_t *large; //连接大内存管理结构 ngx_pool_cleanup_t *cleanup; //清理对象头 ngx_log_t *log; };
//创建一个size的内存池 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); //以对齐的方式来申请size字节内存 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; //初始时,下一个可用内存为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; }
//销毁内存池 void ngx_destroy_pool(ngx_pool_t *pool) { ngx_pool_t *p, *n; ngx_pool_large_t *l; ngx_pool_cleanup_t *c; //运行清理对象的handler 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) { ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc); if (l->alloc) { ngx_free(l->alloc); //使用free释放malloc申请的内存 } } #if (NGX_DEBUG) /* * we could allocate the pool->log from this pool * so we cannot use this log while free()ing the pool */ for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p, unused: %uz", p, p->d.end - p->d.last); if (n == NULL) { break; } } #endif //释放每一个申请的内存池对象ngx_pool_t for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { ngx_free(p); if (n == NULL) { break; } } } //重设内存池 void ngx_reset_pool(ngx_pool_t *pool) { ngx_pool_t *p; ngx_pool_large_t *l; //释放大内存 for (l = pool->large; l; l = l->next) { if (l->alloc) { ngx_free(l->alloc); } } //内存池对象,仅仅改变last的指针位置 for (p = pool; p; p = p->d.next) { p->d.last = (u_char *) p + sizeof(ngx_pool_t); //导致所有的内存池对象的可用内存的起始地址偏移都一样 p->d.failed = 0; } pool->current = pool; pool->chain = NULL; pool->large = NULL; }
//分配内存(地址对齐) void * ngx_palloc(ngx_pool_t *pool, size_t size) { u_char *m; ngx_pool_t *p; if (size <= pool->max) { //小内存申请时,以size为标准 p = pool->current; do { m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT); //首先将d.last地址对齐 if ((size_t) (p->d.end - m) >= size) { //可用的内存大于要申请的内存 p->d.last = m + size; //直接更新d.last return m; //直接返回 } p = p->d.next; //否则找下一个可用的内存池对象 } while (p); //没有找到,则要申请新的内存池对象 return ngx_palloc_block(pool, size); } return ngx_palloc_large(pool, size); //大内存申请处理 } //分配内存(地址可以不对齐) void * ngx_pnalloc(ngx_pool_t *pool, size_t size) { u_char *m; ngx_pool_t *p; if (size <= pool->max) { //小内存 p = pool->current; do { m = p->d.last; 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); //申请新内存池对象 } return ngx_palloc_large(pool, size); //大内存 }
//申请新的内存池对象 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); //只有一个ngx_pool_data_t,节省了ngx_pool_t的其余开销 m = ngx_align_ptr(m, NGX_ALIGNMENT); new->d.last = m + size; //可用的内存的起始地址 //如果当前申请内存的失败的次数已经有5次了,第6次,current将会指向新的内存池对象 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; }
//大内存申请处理 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); //直接malloc申请内存 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) { //但是只找4次,第5次直接break,创建大内存的管理结构 break; } } large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); //从内存池对象申请内存 if (large == NULL) { ngx_free(p); return NULL; } large->alloc = p; //指向申请的大内存 //插入large的头 large->next = pool->large; pool->large = large; return p; }
//不管内存大小多大,向操作系统申请内存 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(pool, sizeof(ngx_pool_large_t)); //申请一个大内存管理结构 if (large == NULL) { ngx_free(p); return NULL; } //放入到内存池ngx_pool_t中管理 large->alloc = p; //指向申请的内存 //插入到头部 large->next = pool->large; pool->large = large; return p; }
//释放内存 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; }
Copyright Statement: This article is an original article by the blogger and may not be reproduced without the blogger's permission.
The above introduces the Nginx advanced data structure source code analysis (4) ----- memory pool, including aspects of the content. I hope it will be helpful to friends who are interested in PHP tutorials.