Nginx源码分析 : 内存管理-内存池

一、关键数据结构 :    typedef struct ngx_pool_s        ngx_pool_t;    struct ngx_pool_s {        ngx_pool_data_t       d;    //描述小内存块,当小内存块不足时,会再分配一个ngx_pool_t,挂在d->next        size_t                    max;    // 评估申请内存属于小块还是大块的标准,在x86上默认是4095字节。        ngx_pool_t           *current;        ngx_chain_t          *chain;        ngx_pool_large_t     *large;   // 当用户申请的内存空间大于max,就会分配大块内存。通过ngx_pool_large_t中的next成员链接形成的单链表        ngx_pool_cleanup_t   *cleanup; // 所有待清理的资源(例如需要关闭或者删除的文件)挂在cleanup->next成员链接形成单链表        ngx_log_t            *log;    };
    typedef struct {        u_char               *last;    //指向小内存块未分配的首地址        u_char               *end;    //指向小内存块的尾部        ngx_pool_t        *next;    // 同属于一个内存池的多个小块内存之间,通过next成员链接形成单链表。        ngx_uint_t          failed;    // 每当当前的小块内存由于空闲部分较少而不能满足用户提出的内存申请请求时,failed成员就会加1。当failed成员大于4后,ngx_pool_t的current成员就会移向下一个小块内存,在以后分配内存时,将从下一个小块内存开始遍历。
    } ngx_pool_data_t

    typedef struct ngx_pool_large_s  ngx_pool_large_t;    struct ngx_pool_large_s {        ngx_pool_large_t     *next;   //  所有大块内存通过next指针链接在一起形成单链表        void                 *alloc;           //指向分配的大内存块
    };
    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;              //下一个    };

二、内存池基本操作    1、内存池操作        //创建内存池,调用malloc分配连续内存, 见下面图1        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;        }        
        //销毁内存池        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) {                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;                }            }        }
        //重置内存池, 释放掉分配的内存,重新设置参数,恢复刚创建的时候,不做多介绍        void ngx_reset_pool(ngx_pool_t *pool);        2、内存的分配        *************************************内存分配**************************************        //ngx内存分配,从内存池中分配, 可以见图2        void * ngx_palloc(ngx_pool_t *pool, size_t size)        {        #if !(NGX_DEBUG_PALLOC)            //分配小包,挂在d链上            if (size <= pool->max) {                return ngx_palloc_small(pool, size, 1);            }        #endif
            //分配大包,挂在larger链上            return ngx_palloc_large(pool, size);        }
        //分配小内存块        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);                }
                //找到可以分配的内存地址,返回内存首地址,并设置小内存块可用首地址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);        }
       //新增一块内存池,并返回需要分配的内存        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);
            //alloc一块同等大小的内存池            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;
            //这里有一个机制,到这里了,前面内存池链表上的所有内存池都是不够分配的,就多有的d.failed ++,            //如果超过4次都找不到的,就讲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;
            //alloc一块内存            p = ngx_alloc(size, pool->log);            if (p == NULL) {                return NULL;            }
            //遍历large链表,找到一个未分配的,将p设置到 lager->alloc上, 连续找3次找不到就不找了            n = 0;            for (large = pool->large; large; large = large->next) {                if (large->alloc == NULL) {                    large->alloc = p;                    return p;                }
                if (n++ > 3) {                    break;                }            }
            //没有可用lager节点,分配一个lager对象            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类似,该函数可以分配不对齐的内存长度        void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
        //ngx_pcalloc类似,分配完内存后,初始化内存        void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
                void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
        //释放内存, 只提前释放lager分配的大内存块,alloc置空,便于下次再用        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;        }
    3、随内存池释放而同步释放        ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);        void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);        void ngx_pool_cleanup_file(void *data);        void ngx_pool_delete_file(void *data);        暂不分析,代码挺简单的,还未分析到底用来干啥的

附图 :                                        图1
                                                                                                图2
文章来源: Nginx源码分析 : 内存管理-内存池

人吐槽 人点赞

猜你喜欢

发表评论

用户名: 密码:
验证码: 匿名发表

你可以使用这些语言

查看评论:Nginx源码分析 : 内存管理-内存池