nginx内存池源码分析

1、简述

很多开源项目都有内存池,由于涉及的场景不同,所以大多数开源项目的内存池设计都并不一样。
nginx会为每一个连接创建内存池,连接断开就会释放内存池。
nginx内存池内存的分配区分大小快,代码如下
分配区分大小快

2、数据结构以及接口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 {
    
    
    ngx_pool_cleanup_pt   handler;  
    void                 *data;     //回调参数
    ngx_pool_cleanup_t   *next;
};


typedef struct ngx_pool_large_s  ngx_pool_large_t;

struct ngx_pool_large_s {
    
    
    ngx_pool_large_t     *next;
    void                 *alloc;
};


typedef struct {
    
    
    u_char               *last; //可以被分配的首地址 初始化p+sizeof(ngx_pool_t)
    u_char               *end;  //poll_s的末尾地址(end-待分配的空间)>last  初始化p+分配的size
    ngx_pool_t           *next; //下一个poll块
    ngx_uint_t            failed;   //申请内存重试失败次数,超过四次,current指向下一个节点
} ngx_pool_data_t;


struct ngx_pool_s {
    
    
    ngx_pool_data_t       d;        //小块内存
    size_t                max;      //大小内存的分界区,默认4k
    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);
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);
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);


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_ */

3、实现细节点

ngx_pfree并没有对小块进行释放,零碎的内存是相对比较耗时的(涉及list的遍历),大块链表的节点也在小块内存上,所以这里并没有销毁大块链表的结构
ngx_destroy_pool 先进行cleanup回调清理(比如limit_conn模块清理红黑树或者fd等标记),然后释放所有的大小快
ngx_reset_pool 释放大块,重置小块内存进行复用,所以这里需要销毁大块内存的链表,最后的large=NULL,目前源码应用geo负载均衡模块

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

清理cleanup
limit_conn模块中使用

static ngx_int_t
ngx_stream_limit_conn_handler(ngx_stream_session_t *s){
    
       
	//.....
	cln = ngx_pool_cleanup_add(s->connection->pool,
							   sizeof(ngx_stream_limit_conn_cleanup_t));
	if (cln == NULL) {
    
    
		return NGX_ERROR;
	}

	cln->handler = ngx_stream_limit_conn_cleanup;
	lccln = cln->data;
	
	//.....
}

static void
ngx_stream_limit_conn_cleanup(void *data)
{
    
    
    ngx_stream_limit_conn_cleanup_t  *lccln = data;

    ngx_slab_pool_t               *shpool;
    ngx_rbtree_node_t             *node;
    ngx_stream_limit_conn_ctx_t   *ctx;
    ngx_stream_limit_conn_node_t  *lc;

    ctx = lccln->shm_zone->data;
    shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr;
    node = lccln->node;
    lc = (ngx_stream_limit_conn_node_t *) &node->color;

    ngx_shmtx_lock(&shpool->mutex);

    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, lccln->shm_zone->shm.log, 0,
                   "limit conn cleanup: %08Xi %d", node->key, lc->conn);

    lc->conn--;

    if (lc->conn == 0) {
    
    
        ngx_rbtree_delete(ctx->rbtree, node);
        ngx_slab_free_locked(shpool, node);
    }

    ngx_shmtx_unlock(&shpool->mutex);
}

猜你喜欢

转载自blog.csdn.net/yc7369/article/details/124597297