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;
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_pool_s ngx_pool_t;
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_free并没有对小块进行释放,零碎的内存是相对比较耗时的(涉及list的遍历)
ngx_destroy_pool 先进行cleanup回调清理(清理红黑树或者fd等标记),然后释放所有的大小快
ngx_reset_pool 释放大块,重置小块内存进行复用,目前源码应用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;
}
清理cleanup
limit_conn模块中使用
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);
}