nginx memory pool source code analysis

1. Brief introduction

Many open source projects have memory pools. Due to the different scenarios involved, most open source projects have different memory pool designs.
Nginx will create a memory pool for each connection, and the memory pool will be released when the connection is disconnected.
The allocation of memory in the nginx memory pool is divided into different sizes, and the code is as follows
Allocation is size-sensitive

2. Data structure and interface ngx_palloc.h:

The memory pool data structure diagram is as follows:
memory pool data structure


/*
 * 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. Implementation details

ngx_pfree does not release small blocks. Fragmented memory is relatively time-consuming (involving list traversal), and the nodes of the large-block linked list are also in small-block memory, so there is no structure for destroying the large-block linked list. ngx_destroy_pool
first cleanup callback cleanup (for example, limit_conn module cleans red-black tree or fd and other tags), and then releases all the size fast
ngx_reset_pool releases large blocks, resets small blocks of memory for multiplexing, so here you need to destroy the linked list of large blocks of memory, the last large =NULL, the current source application geo load balancing module

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 used in the cleanup
limit_conn module

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

Guess you like

Origin blog.csdn.net/yc7369/article/details/124597297