nginx 内存池

来源:互联网 发布:python俄罗斯方块 编辑:程序博客网 时间:2024/05/23 01:18

    nginx的设计思想是小块内存用自己的内存池pool分配,大块内存用malloc分配,并且大块内存和小块内存的管理结构都存储在pool中,由他们管理.

    nginx采用多次分配,一次释放,即一次connect或request完成后,大块小块内存一同释放.

 

看一下内存池的基本数据结构: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;    //数据块最大的值    ngx_pool_t           *current; //指向当前内存池    ngx_chain_t          *chain;       ngx_pool_large_t     *large; //指向大块内存分配    ngx_pool_cleanup_t   *cleanup; //析构函数,当内存释放时候调用    ngx_log_t            *log;};

 

typedef struct {    ngx_fd_t              fd;    u_char               *name;    ngx_log_t            *log;} ngx_pool_cleanup_file_t;

//创建内存池,创建完成后如上图.ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log){    ngx_pool_t  *p;     //内存分配函数,分配大小为size,返回指针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);    //设置max.(比max大的是大内存块.)    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;} 
//销毁内存池.内存池销毁后就完全不存在了.voidngx_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) {        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);        if (l->alloc) {            ngx_free(l->alloc);        }    }#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     //销毁内存池    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {        ngx_free(p);        if (n == NULL) {            break;        }    }} 
//重置内存池,重置后会内存池链表中数据区都为空,即可用.voidngx_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);        }    }     pool->large = NULL;    //将每块pool的last指向数据区的起始位置    for (p = pool; p; p = p->d.next) {        p->d.last = (u_char *) p + sizeof(ngx_pool_t);    }} 

 

//分配内存池,从内存池链表中找到一块内存,交给用户void *ngx_palloc(ngx_pool_t *pool, size_t size){    u_char      *m;    ngx_pool_t  *p;     if (size <= pool->max) { //申请小块内存         p = pool->current;        do {            m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);            if ((size_t) (p->d.end - m) >= size) {                p->d.last = m + size;                return m;            }            p = p->d.next;        } while (p);        //由于已有的内存块不够,另外开辟一块新pool        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, *current;    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;     current = pool->current;    //是指pool的current为最后一个failed大于4的内存块.    /*    我们仔细看一下代码,p->d.failed++是持续的,    即后面pool中的failed肯定>=前面的failed    所以这里的"最后一个failed大于4"一定表示前面的都大于4    所以current指向第一个failed小于4的pool内存块    **/    for (p = current; p->d.next; p = p->d.next) {        if (p->d.failed++ > 4) {            current = p->d.next;        }    }    p->d.next = new;    pool->current = current ? current : new;    return m;}


申请一个新的内存块,申请并挂接到内存池链表中后如图:

 

 

//分配大的内存块,直接用malloc.大块内存的管理结构存储在pool中的数据区.static void *ngx_palloc_large(ngx_pool_t *pool, size_t size){    void              *p;    ngx_uint_t         n;    ngx_pool_large_t  *large;    //用malloc申请内存    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;        }    }//没找到一个可用的大块内存管理结构,//那么在pool池中申请一块内存用于存储该管理结构    large = ngx_palloc(pool, sizeof(ngx_pool_large_t));    if (large == NULL) {        ngx_free(p);        return NULL;    }//将该管理结构链入large中//每次新申请的大块内存管理结构总是插入链表的前面.    large->alloc = p;    large->next = pool->large;    pool->large = large;     return p;} 

 

 

解析一下上图:

1.大块内存管理结构存放在pool池中.

2.大块内存的数据区是malloc开辟出来的,不在pool池中.

3.大块内存管理结构是一个链表,将所有大块内存管理结构都连接起来.

//创建大块内存,并挂载到large链表中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;    }//    在pool的数据区创建大块内存管理结构     large = ngx_palloc(pool, sizeof(ngx_pool_large_t));    if (large == NULL) {        ngx_free(p);        return NULL;    }//将大块内存的管理结构挂载到large链表中    large->alloc = p;    large->next = pool->large;    pool->large = large;     return p;} 
//释放大内存块ngx_int_tngx_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;}
//添加cleanup函数ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size){    ngx_pool_cleanup_t  *c;//在pool池的数据区中创建cleanup管理结构    c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));    if (c == NULL) {        return NULL;    }//在pool池的数据区中分配cleanup的数据    if (size) {        c->data = ngx_palloc(p, size);        if (c->data == NULL) {            return NULL;        }     } else {        c->data = NULL;    }//将新分配的cleanup节点链入cleanup链表中    c->handler = NULL;    c->next = p->cleanup;//注册cleanup函数    p->cleanup = c;     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);     return c;} 

 

//关闭cleanup中的文件描述字voidngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd){   ngx_pool_cleanup_t       *c;    ngx_pool_cleanup_file_t  *cf;    //调用ngx_pool_cleanup_file (就是关闭文件描述字)    for (c = p->cleanup; c; c = c->next) {        if (c->handler == ngx_pool_cleanup_file) {             cf = c->data;             if (cf->fd == fd) {                c->handler(cf);                c->handler = NULL;                return;            }        }    }}
//cleanup结构体typedef struct {    ngx_fd_t              fd; //文件描述字    u_char               *name; //名称    ngx_log_t            *log; } ngx_pool_cleanup_file_t;

 

nginx的内存池释放

        nginx只提供给了用户申请内存的接口,却没有释放内存的接口,那么nginx是如何完成内存释放的呢?总不能一直申请,用不释放啊。针对这个问题,nginx利用了web server应用的特殊场景来完成.

    一个web server总是不停的接受connection和request,所以nginx就将内存池分了不同的等级,有进程级的内存池、connection级的内存池、request级的内存池。每次创建一个内存池链表,每个内存池链表中所有内存池节点大小都是相等的,但是不同的链表中链表节点可能不同.也就是说,创建好一个worker进程的时候,同时为这个worker进程创建一个内存池,待有新的连接到来后,就在worker进程的内存池上为该连接创建起一个内存池;连接上到来一个request后,又在连接的内存池上为request创建起一个内存池。

    这样,在request被处理完后,就会释放request的整个内存池,连接断开后,就会释放连接的内存池。因而,就保证了内存有分配也有释放。

 

0 0
原创粉丝点击