nginx学习_内存池ngx_pool_t

一:设计思路

     nginx内存池的主要思路是预先申请一块空间,以后每次分配小块内存时从这块空间中划分,从而减少了内存申请次数,并避免产生过多的内存碎片.

     当申请大块内存(大于阈值或预申请空间大小)时, 则向系统单独申请新的空间.   阈值为内存分页大小,由系统调用getpagesize()获得

     释放时如果是大块内存,则直接释放,小块内存则不进行处理.

二,结构详解

注: 按nginx命名惯例, ngx_xxx_t 定义时都写为 ngx_xxx_s 再typedef成 nginx_xxx_t

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

参数说明:

1,ngx_pool_data_t d; 管理预申请的内存块,本文称为block. 它是一个链表,当内存池空间不足时会进行扩容, 每次扩容生成一个新的block,它们组成一个链表.

2,max: 用来界定大块内存和小块内存的阈值. 它是系统阈值和预申请空间的较小者.

3 current指针, 当前工作的ngx_pool_data_t, 如前所述,d 是一个链表,每次申请空间时会选取一块合适的预申请空间进行分配,但这个选取并不是从链表头部开始,而是从current开始

4, chain 这个参数好像没有用到

5,large : 这是一个链表,用来管理向系统申请的大块内存.

6,cleanup: 这个好像是用来管理通过内存池申请的套接字等文件,在destroy的时候关闭它们,本文不进行讨论

7, log  nginx的log系统, 本文不讨论.


ngx_pool_data_t 结构定义:

typedef struct {
    u_char               *last;
    u_char               *end;
    ngx_pool_t           *next;
    ngx_uint_t            failed;
} ngx_pool_data_t;

参数说明:

ngx_pool_data_t 用来管理每一块(block)预申请的内存

1,last: 上一次分配内存的地址

2,end: 所申请内存空间的结尾地址

3,next: 下一块预申请空间

4,failed: 分配空间的失败次数, 每当向pool申请小块空间时,如果所有当前block都分配失败,就会申请新的block, 这是每个block.failed 加1,如果这个数值超过4, current指针就会指向这个block.next. 因为每次分配内存从current开始,所以pool将不再尝试从failed>4的block中申请空间


ngx_pool_large_t 结构定义:

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

参数说明:

ngx_pool_large_t 用来管理单独向系统申请的大块内存

1,next 下一个large 元素

2,alloc 所申请的内存空间


三:主要API

(1),创建 ngx_create_pool

ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
     1,通过ngx_memalign申请内存, 根据操作系统的不同调用posix_memalign(freebsd) , malloc(early version)等

     2,根据所申请空间初始化第一个block,并将current指向它.

     3,计算max, 如前文所述,是size和系统阈值的较小者

     4,将large等指针置空

     初始化后的内存分配如图所示:

nginx学习_内存池ngx_pool_t_第1张图片


(2),申请空间 ngx_palloc

void *ngx_palloc(ngx_pool_t *pool, size_t size);
   1,根据max判断申请的是大块内存还是小块内存

   2,如果是大块内存,通过 ngx_palloc_large 单独向系统申请

   3,如果是小块内存,则从current开始遍历,如果有block的可用空间 > size, 就直接分配

   4,如果所有block都不能完成分配, 则通过 ngx_palloc_block 创建新的block并进行分配

(2.1) 静态函数 ngx_palloc_block  

该函数和下面的ngx_palloc_large 是static 函数,不能被外部访问

static void * ngx_palloc_block(ngx_pool_t *pool, size_t size)
   1,新申请一块内存, 大小与第一块相等, 建立一个新的block插在链表d的尾部

   2,对current及current之后的所有block.faild +1, 如果 failed>4 , current 指向其下一个block

   3,在新block上分配空间

(2.2) 静态函数ngx_palloc_large 

static void * ngx_palloc_large(ngx_pool_t *pool, size_t size)
   1,申请一块大小为size的内存p

   2,遍历large链表的前3个元素, 如果其alloc为NULL, 则将其指向p 并返回. alloc为空的large元素是在ngx_pfree的时候产生的,但nginx只检查前3个large元素

   3,新建一个ngx_pool_large_t ,将p赋给它的alloc,并插入large链表的首部. 


(3) 释放函数 ngx_pfree 

ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p)
   1,如果p出现在large链表中,就释放该节点的alloc并置NULL, 注意并不会释放该节点本身,因此会产生上文所说的alloc的large节点, 返回NGX_OK(0)

   2, 否则说明p是小块内存, 不做任何操作并返回NGX_DECLINED (一个负值)

(4) 重置函数 ngx_reset_pool

void ngx_reset_pool(ngx_pool_t *pool)
  1,释放所有large节点的alloc成员, 并释放large节点本身

  2,将每个block置成初始状态, 即将last指针提前至ngx_pool_t结构体本身之后


(5)销毁函数ngx_destroy_pool

void ngx_destroy_pool(ngx_pool_t *pool)
  1,销毁每个large节点

  2,销毁每个block


四:其他

(1), 每个large节点和本文未讨论的cleanup等结构体本身都是由pool申请的, 所以销毁时都是先销毁这些再销毁block

(2) 关于内存对齐问题, 简单来说内存对齐是指申请的空间一定要被2的n次整除,即后n位为0, 这由硬件和os决定,可以提高寻址速度, 详情请baidu. 通过ngx_palloc申请的空间都是有内存对齐的, 但这回损失一定空间, nginx_pnalloc则不会进行对齐, 除了这一点与ngx_pnalloc相同

(3) 类似calloc, nginx也提供了ngx_pcalloc函数, 只是一个初始化了空间的ngx_palloc

(4) 我的nginx 源码版本是 nginx-1.4.7 for redhat 

你可能感兴趣的:(C++,nginx,内存,内存池)