nginx源码阅读(六).内存池

前言

本小节中,我们将看到内存池的实现。由于nginx是由c语言实现,并没有垃圾回收机制,比较容易导致memory leak,因此nginx实现了自己的内存池,将内存的管理和释放交给内存池,而需要申请内存的地方直接使用内存池来申请内存即可,每个连接/请求都会新建一个内存池。

既然释放内存是由内存池来释放,那么何时释放也是一个问题,nginx中的做法是在内存池销毁的时候也将内存释放了,这样可行是因为nginx是一个web服务器,每个请求的生存周期都比较短暂,不会造成一个内存池申请了大量的内存但是本来早就可以释放了却因为生命周期比较长,造成内存得不到及时释放。

内存池相关的结构体

nginx的内存池根据申请的内存是大块还是小块有不同的处理机制。而内存池相关的结构体在src/core/ngx_palloc.h中定义

管理内存池的核心结构体struct ngx_pool_s的定义如下:

typedef struct ngx_pool_s         ngx_pool_t;

struct ngx_pool_s {
  /* 管理小块的内存池。
   * 当该内存池中的空间不足时,会再分配一个ngx_pool_data_t,并使用ngx_pool_data_t结构体中的成员next连接起来
   * 最终形成一个单链表
   */
  ngx_pool_data_t       d;
  /* 该值用于判断申请的内存属于大块还是小块 */
  size_t                max;
  /* 当有多个小块内存池形成链表时,current指向分配内存时第一个小块内存池
   * 其实意思就是指向链表中可以用于分配内存的第一个内存池
   * 这样就不用在分配时从头开始遍历了
   */
  ngx_pool_t           *current;

  ngx_chain_t          *chain;
  /* 申请大块的内存直接从堆中分配
   * 由于需要在内存池释放时同时也要释放内存
   * 因此需要管理分配的大块内存
   * 于是就把每次分配的大块内存通过large组成一个单链表
   */
  ngx_pool_large_t     *large;
  /* 将所有需要释放的资源(比如文件等)通过cleanup组成一个单链表 */
  ngx_pool_cleanup_t   *cleanup;
  /* 内存池执行中输出日志的对象 */
  ngx_log_t            *log;
};

关于大/小块的区分标准,是由NGX_MAX_ALLOC_FROM_POOL宏定义决定的:#define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1)ngx_pagesize是内存中每页的大小,x86下是4KB,即4096(可通过getpagesize()获取每页大小)。由此可见,在不同的机器上,小块内存和大块内存之间的界线并不是一个定值。当申请的内存小于NGX_MAX_ALLOC_FROM_POOL + sizeof(ngx_pool_t)时,则为小块内存,需要通过ngx_pool_data_t d来管理;反之,则在堆中分配,通过large管理。

接下来,我们来看看管理小块内存的ngx_pool_data_t结构体:

typedef struct {
  //指向未分配的空闲内存的首地址
  u_char             *last;
  //指向当前小块内存池的末尾
  u_char             *end;
  /* 指向下一个小块内存池
   * 注意next成员的数据类型并不是ngx_pool_data_t,而是ngx_pool_t
   */
  ngx_pool_t         *next;
  /* 当剩余的空间不足以分配出小块内存时,failed加1
   * 当failed大于4后,ngx_pool_t的current指针会指向下一个小块内存池
   */
  ngx_uint_t          failed;
}ngx_pool_data_t;

接下来,ngx_pool_large_t结构体:

typedef struct ngx_pool_large_s   ngx_pool_large_t;

struct ngx_pool_large_t {
  //指向下一块大的内存
  ngx_pool_large_t        *next;
  //指向调用ngx_alloc分配的大块内存
  void                    *alloc;
}

ngx_pool_t中有个成员是ngx_pool_cleanup_t *cleanup,用于释放比如文件等非内存的资源,定义如下:

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;
  //handler指向的方法的参数
  void                   *data;
  //指向下一个ngx_pool_cleanup_t结构体
  ngx_pool_cleanup_t     *next;
};

关于内存池相关的结构体就这些,其实通过这些结构体,我们就已经对nginx内存池运作的机制有了一定了解。
接下来我们看看内存池是如何创建、销毁、重置以及分配、释放等。

创建内存池

size参数代表的大小包括了ngx_pool_t结构体的大小,因此若sizengx_pool_t还小,可能会导致内存越界(因为会访问ngx_pool_t *p,若没有分配空间,则内存越界)。

ngx_pool_t *
ngx_create_pool(size_t size, ngx_log_t *log)
{
    ngx_pool_t  *p;

    /* NGX_POOL_ALIGNMENT在core/ngx_palloc.h中宏定义为16
     * ngx_memalign的作用就是将size进行16字节的对齐并分配空间(内部调用memalign对齐并分配空间)
     * malloc/realloc返回的内存地址都是以8字节对齐,如果想要更大粒度的对齐,则可以调用memalign函数
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值