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 は小さなブロックを解放しません。断片化されたメモリは比較的時間がかかり (リストのトラバーサルを含む)、大きなブロックのリンクされたリストのノードも小さなブロックのメモリ内にあるため、大きなブロックのリンクされたリストを破棄するための構造がありません。 ngx_destroy_pool
最初のクリーンアップ コールバック クリーンアップ (たとえば、limit_conn モジュールは赤黒ツリーまたは fd およびその他のタグをクリーンアップします)、その後すべてのサイズを高速に解放します ngx_reset_pool は大きなブロックを解放し、多重化のためにメモリの小さなブロックをリセットします
。大きなメモリ ブロックのリンク リストと、現在のソース アプリケーションの geo ロード バランシング モジュールである最後の大きな =NULL を破棄します。

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


クリーンアップ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