nginx源码解析之内存池

nginx自身实现了内存池,所有内存分配都是基于内存池来操作。基本思想是预申请一段内存空间,,低于指定大小的内存(小段内存)直接从内存池中申请,超过指定大小的内存(大段内存)直接调用malloc申请。相关代码在os/unix/ngx_alloc.{c,h}和core/ngx_palloc.{c,h}。

os/unix/ngx_alloc.{c,h}文件封装了内存分配的系统调用,其中:

core/ngx_palloc.{c,h}包含了nginx内存池实现的主要代码,先看看内存池的相关数据结构:

{u_char*last; // 内存池可用空间起始地址u_char*end; // 内存池末端地址ngx_pool_t*next;ngx_uint_tfailed; // 尝试分配失败次数} ngx_pool_data_t;typedef struct ngx_pool_large_s ngx_pool_large_t;struct ngx_pool_large_s {ngx_pool_large_t*next;void*alloc; // 大段内存空间,调用malloc申请};struct ngx_pool_s {ngx_pool_data_td;size_tmax; // 内存池分配空间(不超过page_size-1)ngx_pool_t*current; // 当前内存池ngx_chain_t*chain;ngx_pool_large_t*large; // 大段内存链表ngx_pool_cleanup_t *cleanup;ngx_log_t*log;};

转换成为数据结构图可能更加清晰一些:

注意图中current指针指向的并不是所在的内存池,实际上current指针的指向是会变化的。

依次查看ngx_palloc.c文件中的代码:

ngx_create_poll函数用来创建size大小的内存池ngx_pool_t *p;// 申请NGX_POLL_ALIGNMENT对齐的内存空间p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);// 实际可分配的空间为size-sizeof(ngx_poll_t)p->d.last = (u_char *) p + sizeof(ngx_pool_t);p->d.end = (u_char *) p + size;size = size – sizeof(ngx_pool_t);p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;…ngx_destroy_poll函数用来销毁内存池,销毁流程是先销毁cleanup链表中的内存(数据结构定义如下,即调用handler函数销毁data空间),销毁大段内存(large链表),最后销毁ngx_poll_t链表。typedef void (*ngx_pool_cleanup_pt)(void *data);typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t;struct ngx_pool_cleanup_s {ngx_pool_cleanup_pt handler;void*data;ngx_pool_cleanup_t *next;};ngx_reset_pool函数用来重置内存池(销毁大段内存空间、重置内存池字段,如last字段设为p+sizeof(ngx_poll_t))ngx_palloc函数从内存池中申请一段空间并返回地址void *ngx_palloc(ngx_pool_t *pool, size_t size){u_char*m;ngx_pool_t *p;) {p = pool->current;do {// 计算last地址对齐后的地址m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);// 可用空间超过size则直接返回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);}ngx_pnalloc函数与ngx_palloc函数功能相同,唯一不同的是缺少了计算last对齐后的地址,即ngx_palloc返回的一定是内存地址对齐的地址,而ngx_pnalloc则不一定。do {// 唯一与ngx_palloc不同的地方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);ngx_palloc_block函数在ngx_palloc和ngx_pnalloc中均有被调用,用来重新申请内存池并返回对齐的地址空间static void *ngx_palloc_block(ngx_pool_t *pool, size_t size){u_char*m;size_tpsize;ngx_pool_t *p, *new, *current;// 计算当前内存池分配大小psize = (size_t) (pool->d.end – (u_char *) pool);// 申请内存地址对齐的空间m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);if (m == NULL) {return NULL;}(ngx_pool_t *) m;new->d.end = m + psize;;;m += sizeof(ngx_pool_data_t);m = ngx_align_ptr(m, NGX_ALIGNMENT);new->d.last = m + size;// 重新设置current指针,将刚申请的内存池放到链表末尾current = pool->current;for (p = current; p->d.next; p = p->d.next) {) {current = p->d.next;}}p->d.next = new;pool->current = current ? current : new;return m;}ngx_palloc_large用来申请大段内存空间(大小超过内存池空间)static void *ngx_palloc_large(ngx_pool_t *pool, size_t size){void*p;ngx_uint_tn;ngx_pool_large_t *large;// 调用malloc申请空间p = ngx_alloc(size, pool->log);if (p == NULL) {return NULL;}n = 0;// 尝试将申请的空间放入large链表中(仅尝试4次?)for (large = pool->large; large; large = large->next) {if (large->alloc == NULL) {large->alloc = p;return p;}) {break;}}// 申请ngx_pool_large_t数据结构,初始化alloc指向申请的大段内存,放到large链表头large = ngx_palloc(pool, sizeof(ngx_pool_large_t));if (large == NULL) {ngx_free(p);return NULL;}large->alloc = p;large->next = pool->large;pool->large = large;return p;}ngx_pmemalign函数用来申请内存地址对齐的大段空间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;}large->alloc = p;large->next = pool->large;pool->large = large;return p;}ngx_pfree函数用来释放指定内存空间(针对大段空间)ngx_int_tngx_pfree(ngx_pool_t *pool, void *p){ngx_pool_large_t *l;// 遍历large链表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;}ngx_pcalloc函数在ngx_palloc的基础上,将这段空间初始化为0

ngx_pool_cleanup_add、ngx_pool_run_cleanup_file、ngx_pool_cleanup_file和ngx_pool_delete_file函数暂时不对其进行解析。以上便是nginx内存池实现的全部,本代码基于nginx1.7.0,在CentOS6.0下完成编译。

每个人在他的人生发轫之初,总有一段时光,

nginx源码解析之内存池

相关文章:

你感兴趣的文章:

标签云: