nginx源码分析——内存池

Posted hvicen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nginx源码分析——内存池相关的知识,希望对你有一定的参考价值。

ngx_palloc.h

/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) nginx, Inc.
 */


#ifndef _NGX_PALLOC_H_INCLUDED_
#define _NGX_PALLOC_H_INCLUDED_


#include <ngx_config.h>
#include <ngx_core.h>


// 内存池界定小内存与大内存的边界值的最大值。
/*
 * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
 * On Windows NT it decreases a number of locked pages in a kernel.
 */
#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)

// 未知 #define NGX_DEFAULT_POOL_SIZE (16 * 1024) // 内存池的内存对齐值,即分配的内存大小是该值的倍数。 #define NGX_POOL_ALIGNMENT 16

// 未知 #define NGX_MIN_POOL_SIZE \ ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)), NGX_POOL_ALIGNMENT) // 回收方法的模型 typedef void (*ngx_pool_cleanup_pt)(void *data); // 回收节点 typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t; struct ngx_pool_cleanup_s { // handler,回收方法 ngx_pool_cleanup_pt handler; // data指针,指向用于回收方法的传入参数 void *data; // next指针,指向下一个回收节点 ngx_pool_cleanup_t *next; }; // 大内存节点 typedef struct ngx_pool_large_s ngx_pool_large_t; struct ngx_pool_large_s { // next指针,指向下一个大内存节点 ngx_pool_large_t *next; // alloc指针,指向大内存 void *alloc; }; // 小内存块节点 typedef struct { // last指针,指向内存块里已用空间的结束位,亦是可用空间的开始位。 u_char *last; // end指针,指向内存块的结束位。 u_char *end; // next指针,指向下一个内存块。虽然类型是内存池,但实际应用时视作内存块类型。 ngx_pool_t *next; // failed,统计内存块因剩余空间不足以达到所需分配的内存大小而失败,且需要新建内存块才能分配所需内存的次数。 // 这个次数达到6次(从0累计到5)时,则下一次分配内存时,不管此内存块的剩余空间是否足够,都不会在此内存块上分配内存。 ngx_uint_t failed; } ngx_pool_data_t; // 内存池 struct ngx_pool_s { // d,内存池的第一个小内存块,意味着内存池自身是存储于第一个小内存块里。 ngx_pool_data_t d; // max边界值,用于界定分配的内存是小内存还是大内存。 // max边界值,先受限于最大界值,再受限于初始内存大小。 size_t max; // current指针,指向第一个可分配内存的小内存块。 ngx_pool_t *current; ngx_chain_t *chain; // large指针,指向大内存节点链头. ngx_pool_large_t *large; // cleanup指针,指向回收节点链头 ngx_pool_cleanup_t *cleanup; ngx_log_t *log; }; // 文件类型的回收节点时,其data所需该类型 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); // 销毁内存池 void ngx_destroy_pool(ngx_pool_t *pool); // 重置内存池 void ngx_reset_pool(ngx_pool_t *pool); // 从内存池里分配内存(对齐内存) void *ngx_palloc(ngx_pool_t *pool, size_t size); // 分配内存(不对齐内存) void *ngx_pnalloc(ngx_pool_t *pool, size_t size); // 从内存池里分配内存(对齐内存),并填充为0。 void *ngx_pcalloc(ngx_pool_t *pool, size_t size); // 从内存池里分配大内存(内存对齐,不使用空闲内存节点) void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment); // 从内存池里释放指定的大内存(不释放节点) ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p); // 从内存池里创建一个回收节点,当size大于0时,将创建给定大小的内存,但这个内存并不受内存池管理,即需要创建者自行释放。 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); #endif /* _NGX_PALLOC_H_INCLUDED_ */

 

 

ngx_palloc.c

/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */


#include <ngx_config.h>
#include <ngx_core.h>


static ngx_inline void *ngx_palloc_small(ngx_pool_t *pool, size_t size,
    ngx_uint_t align);
static void *ngx_palloc_block(ngx_pool_t *pool, size_t size);
static void *ngx_palloc_large(ngx_pool_t *pool, size_t 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);
    if (p == NULL) {
        return NULL;
    }

    // last指针,指向内存块里已用空间的结束位,亦是可用空间的开始位。
    p->d.last = (u_char *) p + sizeof(ngx_pool_t); // 内存池和内存块各自占一部份内存。
    // end指针,指向内存块的结束位。
    p->d.end = (u_char *) p + size;
    p->d.next = NULL;
    p->d.failed = 0;

    // max边界值,用于界定分配的内存是小内存还是大内存。
    // max边界值,先受限于最大界值,再受限于初始内存大小。
    size = size - sizeof(ngx_pool_t);
    p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;

    // current指针,表示当前内存块,当前内存块是处于可分配内存的状态
    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;

    // 处理回收节点
    for (c = pool->cleanup; c; c = c->next) {
        // handler,回收方法,为NULL时表示已回收。
        if (c->handler) {
            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
                           "run cleanup: %p", c);
            // data,用于调用回收方法时传入的参数。
            c->handler(c->data);
        }
    }

// 调试模式时,输出日志
#if (NGX_DEBUG)

    /*
     * we could allocate the pool->log from this pool
     * so we cannot use this log while free()ing the pool
     */

    for (l = pool->large; l; l = l->next) {
        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);
    }

    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 (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)
{
    ngx_pool_t        *p;
    ngx_pool_large_t  *l;

    // 释放大内存
    for (l = pool->large; l; l = l->next) {
        if (l->alloc) {
            ngx_free(l->alloc);
        }
    }

    // 重置小内存
    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)
{
#if !(NGX_DEBUG_PALLOC)
    // max边界值,用于界定分配的内存是小内存还是大内存
    if (size <= pool->max) {
        // 分配小内存(对齐方式)
        return ngx_palloc_small(pool, size, 1);
    }
#endif
    // 分配大内存
    return ngx_palloc_large(pool, size);
}


// 分配内存(不对齐内存)
void *
ngx_pnalloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
    // max边界值,用于界定分配的内存是小内存还是大内存
    if (size <= pool->max) {
        // 分配小内存
        return ngx_palloc_small(pool, size, 0);
    }
#endif
    // 分配大内存
    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);
        }

        // 判断剩余空间是否足够所需内存大小
        if ((size_t) (p->d.end - m) >= size) {
            // 如果足够,则分配此内存块
            // last指针,指向内存块里已用空间的结束位,亦是可用空间的开始位。
            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);
    m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
    if (m == NULL) {
        return NULL;
    }
    new = (ngx_pool_t *) m;

    // end指针,指向新内存块的结束位。
    new->d.end = m + psize;
    new->d.next = NULL;
    new->d.failed = 0;

    // last指针,指向新内存块里已用空间的结束位,亦是可用空间的开始位。
    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) {
        // failed,统计内存块因剩余空间不足以达到所需分配的内存大小而失败,且需要新建内存块才能分配所需内存的次数。
        // 这个次数达到6次(从0累计到5)时,则下一次分配内存时,不管此内存块的剩余空间是否足够,都不会在此内存块上分配内存。
        if (p->d.failed++ > 4) {
            // current指针,指向可分配内存的内存块。
            pool->current = p->d.next;
        }
    }

    // 把新内存块加入链尾
    p->d.next = new;

    // 返回分配的内存
    return m;
}

// 从内存池里分配大内存(私有,使用前5个空闲内存节点)
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) {
            // 如果为空,则绑定内存到该内存节点。
            // alloc指针,指向内存。
            large->alloc = p;
            // 返回内存
            return p;
        }

        // 累计未能找到空的内存块节点的次数,如果达到5次(从0到4),则结束寻找。
        // 意味着空的内存节点最多存在5个。
        if (n++ > 3) {
            break;
        }
    }

    // 创建内存节点
    large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
    if (large == NULL) {
        ngx_free(p);
        return NULL;
    }
    
    // alloc指针,指向内存。
    large->alloc = p;
    // next指针,指向下一个内存节点。
    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_small(pool, sizeof(ngx_pool_large_t), 1);
    if (large == NULL) {
        ngx_free(p);
        return NULL;
    }

    // alloc指针,指向内存。
    large->alloc = p;
    // next指针,指向下一个内存节点。
    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;
}


// 从内存池里分配内存(对齐内存),并填充为0。
void *
ngx_pcalloc(ngx_pool_t *pool, size_t size)
{
    void *p;

    // 创建内存
    p = ngx_palloc(pool, size);
    if (p) {
        // 填充0
        ngx_memzero(p, size);
    }

    return p;
}

// 从内存池里创建一个回收节点,当size大于0时,将创建给定大小的内存,但这个内存并不受内存池管理,即需要创建者自行释放。
ngx_pool_cleanup_t *
ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
{
    ngx_pool_cleanup_t  *c;

    // 创建回收节点
    c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));
    if (c == NULL) {
        return NULL;
    }

    // data,用于调用回收方法时传入的参数。
    if (size) {
        // 创建内存,但这个内存并不受内存池管理,即需要创建者自行释放。
        c->data = ngx_palloc(p, size);
        // BUG?如果创建内存失败,此时的回收节点还未加入到链头,意味着回收节点不能释放?
        if (c->data == NULL) {
            return NULL;
        }

    } else {
        c->data = NULL;
    }
    // handler,回收方法。
    c->handler = NULL;

    // 加入链头
    c->next = p->cleanup;
    p->cleanup = c;

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);

    return c;
}


// 从内存池里清理给定的文件类型的回收节点
void
ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd)
{
    ngx_pool_cleanup_t       *c;
    ngx_pool_cleanup_file_t  *cf;

    for (c = p->cleanup; c; c = c->next) {
        if (c->handler == ngx_pool_cleanup_file) {
            // data,用于调用回收方法时传入的参数。
            cf = c->data;
            if (cf->fd == fd) {
                // handler,回收方法,为NULL时表示已回收。
                c->handler(cf); // 这里实际执行的方法是ngx_pool_cleanup_file
                c->handler = NULL;
                return;
            }
        }
    }
}

// 关闭文件
void
ngx_pool_cleanup_file(void *data)
{
    ngx_pool_cleanup_file_t  *c = data;

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d",
                   c->fd);

    // 执行文件关闭
    // fd,文件句柄
    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                      ngx_close_file_n " \"%s\" failed", c->name);
    }
}


// 删除和关闭文件
void
ngx_pool_delete_file(void *data)
{
    ngx_pool_cleanup_file_t  *c = data;

    ngx_err_t  err;

    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d %s",
                   c->fd, c->name);

    // 执行文件删除
    // name,文件名称
    if (ngx_delete_file(c->name) == NGX_FILE_ERROR) {
        err = ngx_errno;

        if (err != NGX_ENOENT) {
            ngx_log_error(NGX_LOG_CRIT, c->log, err,
                          ngx_delete_file_n " \"%s\" failed", c->name);
        }
    }

    // 执行文件关闭
    // fd,文件句柄
    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                      ngx_close_file_n " \"%s\" failed", c->name);
    }
}


#if 0

static void *
ngx_get_cached_block(size_t size)
{
    void                     *p;
    ngx_cached_block_slot_t  *slot;

    if (ngx_cycle->cache == NULL) {
        return NULL;
    }

    slot = &ngx_cycle->cache[(size + ngx_pagesize - 1) / ngx_pagesize];

    slot->tries++;

    if (slot->number) {
        p = slot->block;
        slot->block = slot->block->next;
        slot->number--;
        return p;
    }

    return NULL;
}

#endif

 




以上是关于nginx源码分析——内存池的主要内容,如果未能解决你的问题,请参考以下文章

Nginx 源码学习内存池 及 优秀案例赏析:Nginx内存池设计

nginx源代码分析之内存池实现原理

内存池 及 nginx内存池

nginx源码分析——线程池

kylin源码分析-内存池

源码分析-使用newFixedThreadPool线程池导致的内存飙升问题