Nginx内存池管理

由于本人才疏学浅,本文难免存在遗漏之处,欢迎大家留言指正,本人将感激不尽。

Nginx使用内存池对内存进行管理,提高内存管理效率。对于小块内存,直接从已分配好的内存池中获取(返回指针);对于大块内存,直接调用malloc申请内存,然后将大块内存挂在内存池上进行管理(链表形式)。

对于内存的申请,Nginx还做了一些优化,比如在某一块内存池上申请小内存失败次数超过四次时,直接跳过该内存池,后续将不在该内存池上申请小块内存;

Nginx内存管理的核心结构体如下:

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;	//下一次申请小块内存的pool
    ngx_chain_t          *chain;
    ngx_pool_large_t     *large;	//保存大块内存的链表
    ngx_pool_cleanup_t   *cleanup;
    ngx_log_t            *log;
};

对于小块内存,全部位于ngx_pool_s的数据区段,ngx_pool_s按链表形式组织;对于大块内存,全部以链表的形式存储,large指向分配的第一个大块内存。如下图所示:
在这里插入图片描述
下面主要通过代码介绍一下内存分配的过程

创建内存池

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_palloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
    if (size <= pool->max) { //分配小块内存,直接在内存池上分配,移动指针
        return ngx_palloc_small(pool, size, 1);
    }
#endif

    return ngx_palloc_large(pool, size); //分配大块内存,调用malloc创建size大小的内存,然后插入large链表
}

分配小块内存如下:

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; //获取当前可申请小块内存的pool
    
	/*在内存池链表中查找可分配小块内存的内存池*/
    do {
        m = p->d.last;
        if (align) {
            m = ngx_align_ptr(m, NGX_ALIGNMENT);
        }
        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);

    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;

    for (p = pool->current; p->d.next; p = p->d.next) {
        if (p->d.failed++ > 4) {	//如果超过四次分配小内存失败,则将current指针后移
            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); //申请大块内存
    if (p == NULL) {
        return NULL;
    }

    n = 0;
    
	//插入large链表中,寻找可插入的位置
    for (large = pool->large; large; large = large->next) {
        if (large->alloc == NULL) {
            large->alloc = p;
            return p;
        }

        if (n++ > 3) { //若连续三次未找到可插入的位置,则直接插入链表头部
            break;
        }
    }

    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;
}
参考

https://www.kancloud.cn/digest/understandingnginx/202588

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值