nginx pool 内存池设计

nginx内存池

  • 为了减少内存碎片的数量,并通过统一管理来减少代码中出现内存泄漏的可能性

  • 内存池 ngx_pool_s

  • 内存块数据 ngx_pool_data_t

  • 大内存块 ngx_pool_large_s

pool数据结构

    struct ngx_pool_large_s {  //大块内存结构体,链表结构 
        ngx_pool_large_t     *next;
        void                 *alloc;//申请的内存块地址   
    };

    typedef struct {    //内存块包含的数据  
        u_char               *last;//申请过的内存的尾地址,可申请的首地址    pool->d.last ~ pool->d.end 中的内存区便是可用数据区。
        u_char               *end;//当前内存池节点可以申请的内存的最终位置  
        ngx_pool_t           *next;//下一个内存池节点ngx_pool_t,见ngx_palloc_block
        ngx_uint_t            failed;//当前节点申请内存失败的次数,   如果发现从当前pool中分配内存失败四次,则使用下一个pool,见ngx_palloc_block 
    } ngx_pool_data_t;

    struct ngx_pool_s { //内存池数据结构,链表形式存储
        ngx_pool_data_t       d;//节点数据,包含 pool 的数据区指针的结构体 pool->d.last ~ pool->d.end 中的内存区便是可用数据区。
        size_t                max;//当前内存节点可以申请的最大内存空间,一次最多从pool中开辟的最大空间
        //每次从pool中分配内存的时候都是从curren开始遍历pool节点获取内存的
        ngx_pool_t           *current;//内存池中可以申请内存的第一个节点,不一定指向第一个,如果第一块申请内存失败次数大于4次,就会移到d->next处。

        ngx_chain_t          *chain;// pool 当前可用的 ngx_chain_t 数据,由函数ngx_alloc_chain_link 创建,由 ngx_free_chain 释放
        ngx_pool_large_t     *large;//节点中大内存块指针,pool 中指向大数据快的指针(指 size > max 的数据块)
        ngx_pool_cleanup_t   *cleanup;// pool 中指向 ngx_pool_cleanup_t 数据块的指针,cleanup在ngx_pool_cleanup_add赋值
        ngx_log_t            *log; // pool 中指向 ngx_log_t 的指针,用于写日志的  ngx_event_accept会赋值
    };

    //内存池pool中清理数据的用的,见ngx_pool_s  ngx_destroy_pool()
    struct ngx_pool_cleanup_s { //这个是添加到ngx_pool_s中的cleanup上的,见ngx_pool_cleanup_add()
        ngx_pool_cleanup_pt   handler;// 当前 cleanup 数据的回调函数ngx_destroy_pool()中执行,例如清理文件句柄ngx_pool_cleanup_file等
        void                 *data;// 内存的真正地址,回调时,将此数据传入回调函数,ngx_pool_cleanup_add()中开辟空间
        ngx_pool_cleanup_t   *next;// 指向下一块 cleanup 内存的指针
    };

pool操作

ngx_create_pool(size,log)

  1. 创建pool,创建一个内存池,使用 ngx_palloc(pool,size) 从其中分配数据。

ngx_palloc/ngx_pnalloc(pool,size)

  • 从pool中分配一块内存

  • 若大于 pool的最大空间,就调用ngx_palloc_large(pool,size)在large中开辟大空间;否则就在内存池中分配内存,遍历这个pool的所有块,如果每个块内存都不够,就调用ngx_palloc_block(pool, size)创建新的块,加到最后一个next上。

  • ngx_palloc_block(pool, size)中有个 失败四次更新current的逻辑。

  • ngx_palloc_large(pool,size)中有个逻辑:在large链表中查找三次没找到就创建个新空间插到链表头。

ngx_pool_cleanup_add(pool,size)

  • 为pool添加cleanup数据

  • 这个只是申请cleanup 头信息,需要自己给handler赋值

  • 一个pool的所有cleanup 是个环形。

ngx_reset_pool(pool)

  • 重置pool中的数据,主要做两个工作:

    1. 把large内存块释放,但链表依然保留。

    2. 把data块指针移到开头,错误次数置0,相当于清空数据,链表保留。

ngx_destory_pool(pool)

  • 销毁 pool

  • 调用所有 cleanup

  • 释放所有large

  • 释放所有data

内存对齐

  • 对齐提高效率,内存的IO是以8个字节64bit为单位进行的,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

  • 内存对齐宏 ngx_align_ptr

  • nginx 内存池的内存地址对齐和长度按照2的幂取整,内存池的里面返回的地址,都是经过对齐处理的。

最后更新于

这有帮助吗?