nginx源码学习(二) 内存池结构 ngx_pool_t

来源:互联网 发布:手机铃声制作软件 mac 编辑:程序博客网 时间:2024/06/05 20:29

1,nginx的内存池介绍

    为了方便系统模块对内存的使用,方便内存的管理,nginx自己实现了进程池的机制来进行内存的分配和释放, 首先nginx会在特定的生命周期帮你

   统一建立内存池,当需要进行内存分配的时候统一通过内存池中的内存进行分配,最后nginx会在适当的时候释放内存池的资源,开发者只要在需要

   的时候对内存进行申请即可,不用过多考虑内存的释放等问题,大大提高了开发的效率。

  例如对于内存的管理,如果我们需要使用内存,那么总是从一个ngx_pool_t的对象中获取内存,在最终的某个时刻,我们销毁这个ngx_pool_t对象,

  所有这些内存都被释放了。这样我们就不必要对对这些内存进行malloc和free的操作,不用担心是否某块被malloc出来的内存没有被释放。因为当

   ngx_pool_t对象被销毁的时候,所有从这个对象中分配出来的内存都会被统一释放掉。

2,nginx内存管理相关结构

 下面介绍下 nginx 封装的一些有关内存分配、管理相关函数,内存相关的操作主要在文件 os/unix/ngx_alloc.{h,c} 和 core/ngx_palloc.{h,c} 中实现:

 A  ./src/os/unix/ngx_alloc.{h,c}中包括所有nginx内存申请的相关函数。

    ngx_alloc() 包装了malloc(),仅添加了内存分配失败时的,log输出和debug时的log输出。

    ngx_calloc() 调用上面的函数,成功分配后,将内存清零。

    ngx_memalign() 也是向操作系统申请内存,只不过采用内存对齐方式。是为了减少内存碎片。如果操作系统支持posix_memalign()就采用它,如果

    支持memalign()则用memalign()。在0.8.19版本中,作者不再使用ngx_alloc(),而全部改用ngx_memalign(),函数ngx_memalign()返回基于一个指

   定alignment的大小为size的内存 空间,且其地址为alignment的整数倍,alignment为2的幂。

 B,./src/core/ngx_palloc.{h,c}

  包含了内存管理的数据结构,封装创建/销毁内存池,从内存池分配空间等函数。

  内存管理中几个主要的数据结构:
  在文件ngx_palloc.h定义的内存管理相关数据结构。

//该结构用来维护内存池的数据块,供用户分配之用typedef struct {    u_char               *last;  //当前内存分配结束位置,即下一段可分配内存的起始位置    u_char               *end;   //内存池结束位置    ngx_pool_t           *next;  //内存池里面有很多块内存,这些内存块就是通过该指针连成链表的    ngx_uint_t            failed;//统计该内存池不能满足分配请求的次数,即分配失败次数} ngx_pool_data_t;//内存池的数据块位置信息 

ngx_pool_t结构

typedef struct ngx_pool_s        ngx_pool_t;//该结构维护整个内存池的头部信息struct ngx_pool_s {    ngx_pool_data_t       d;       //数据块    size_t                max;     //内存池数据块的最大值   ngx_pool_t           *current; //指向当前内存池  ngx_chain_t          *chain;   //该指针挂接一个ngx_chain_t结构      ngx_pool_large_t     *large;   //分配大块内存用,即超过max的内存请求    ngx_pool_cleanup_t   *cleanup; //释放内存池的callback    ngx_log_t            *log;   //日志信息};

其中,sizeof(ngx_pool_data_t)=16Bsizeof(ngx_pool_t)=40B

大块内存结构:

//大内存结构struct ngx_pool_large_s {    ngx_pool_large_t     *next; //下一个大块内存    void                 *alloc;//指向分配的大块内存};

当待分配空间已经超过了池子自身大小,nginx也没有别的好办法,只好按照你需要分配的大小,实际去调用malloc()函数去分配,例如池子的大小是1K,

待分配的大小是1M。实际上池子里只存储了ngx_pool_large_t结构,这个结构中的alloc指针,指向被分配的内存,并把这个指针返回给系统使用。

上述这些数据结构的逻辑结构图如下:

          

  :在nginx的main()函数中,通过将ngx_pagesize 设置为1024来指定内存 分配按1024bytes对齐。这意味着你虽指示分配10 bytes的内存,

  实际上nginx也向 操作系统申请至少1024bytes的内存。 nginx将几乎所有的结构体放在ngx_core.h文件中重新进行了申明,形式如:

  typedef struct ngx_**_s      ngx_**_t;

3,nginx 内存池操作相关。

 A,内存池的创建销毁
               

//创建内存池ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log){    ngx_pool_t  *p;   // ngx_memalign 返回值为void*,void*可以执指向任何类型的数据    p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);  // 内存分配,该函数的实现在src/os/unix/ngx_alloc.c文件中,uinx,windows分开走    if (p == NULL) {        return NULL;    }    p->d.last = (u_char *) p + sizeof(ngx_pool_t); //last指向ngx_pool_t结构体之后数据的起始    p->d.end = (u_char *) p + size; //end指向分配的整个size大小的内存的末尾     p->d.next = NULL;    p->d.failed = 0;   //max中存放的数指所申请内存块中空闲的大小,因此,在计算max之前先减去了管理结点本身的大小。     size = size - sizeof(ngx_pool_t); //最大不超过 NGX_MAX_ALLOC_FROM_POOL,也就是getpagesize()-1 大小,即4095B    p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;     p->current = p;    p->chain = NULL;    p->large = NULL;    p->cleanup = NULL;    p->log = log;    return p;}

创建一个初始节点大小为size的pool,log为后续在该pool上进行操作时输出日志的对象。 需要说明的是size的选择,size的大小必须小于等于NGX_MAX_ALLOC_FROM_POOL 且必须大于sizeof(ngx_pool_t)。

选择大于NGX_MAX_ALLOC_FROM_POOL的值会造成浪费,因为大于该限制的空间不会被用到(只是说在第一个由ngx_pool_t对象管理的内存

块上的内存,后续的分配如果第一个内存块上的空闲部分已用完,会再分配的)。选择小于sizeof(ngx_pool_t)的值会造成程序崩溃。由于初始大

小的内存块中要用一部分来存储ngx_pool_t这个信息本身。当一个ngx_pool_t 对象被创建以后,该对象的max字段被赋值为size-sizeof(ngx_pool_t)

和NGX_MAX_ALLOC_FROM_POOL这两者中比较小的。后续的从这个pool中分配的内存块,在第一块内存使用完成以后,如果要继续分配的话,

就需要继续从操作系统申请内存。当内存的大小小于等于max字段的时候,则分配新的内存块,链接在d这个字段(实际上是d.next字段)管理的一

条链表上。当要分配的内存块比max大的,那么从系统中申请的内存是被挂接在large字段管理的一条链表上。我们暂且把这个称之为大块内存链

和小块内存链。

总结起来,ngx_create_pool有如下三步:

第一步,调用ngx_memalign()申请内存;

第二步,设置ngx_pool_t中的成员d(即类型ngx_pool_data_t)中的各个变量;
  …
  p->d.last = (u_char *) p + sizeof(ngx_pool_t);
  p->d.end = (u_char *) p + size;
  p->d.next = NULL;
  p->d.failed = 0;
  d.last则指向未使用内存的开始处,而d.end指向内存块的结尾处。刚申请的内存中占用ngx_pool_t结构体作为管理单元。所以,此时d.last指向

  (u_char *) p + sizeof(ngx_pool_t)处。

第三步,设置ngx_pool_t 除d变量的其它成员;
    ...
    p->max =...
    p->current = p;
    p->chain = NULL;
    p->large = NULL;
    p->cleanup = NULL;
     ...
在计算max时,max中存放的数指所申请内存块中空闲内存的大小。因此,在计算max之前先减去了管理结点本身的大小。

 下面介绍 ngx_create_pool 里分配内存的 ngx_memalign()函数

 void *  ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)  {      void  *p;      int    err;            err = posix_memalign(&p, alignment, size);      //该函数分配以alignment为对齐的size字节的内存大小,其中p指向分配的内存块。            if (err) {          ngx_log_error(NGX_LOG_EMERG, log, err,              "posix_memalign(%uz, %uz) failed", alignment, size);          p = NULL;      }            ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,          "posix_memalign: %p:%uz @%uz", p, size, alignment);            return p;  }  

  从这个函数的实现体,我们可以看到p =ngx_memalign(NGX_POOL_ALIGNMENT, size, log);

  函数分配以NGX_POOL_ALIGNMENT字节对齐的size字节的内存,在src/core/ngx_palloc.h

  #defineNGX_POOL_ALIGNMENT  16  因此,nginx的内存池分配,是以16字节为边界对齐的

  首先我们要理解为什么要内存对齐?  为了移植性和 程序的性能,可以参考这篇文章。


 看下内存池的销毁函数,pool指向需要销毁的内存池:

    void      ngx_destroy_pool(ngx_pool_t *pool)      {          ngx_pool_t          *p, *n;          ngx_pool_large_t    *l;          ngx_pool_cleanup_t  *c;          // 遍历 cleanup链表结构依次调用clenup的handler清理     //cleanup指向析构函数,用于执行相关的内存池销毁之前的清理工作,如文件的关闭等.        for (c = pool->cleanup; c; c = c->next) {              if (c->handler) {                  ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,                      "run cleanup: %p", c);                  c->handler(c->data);              }          }          //清理大块内存,ngx_free实际上就是标准的free函数        for (l = pool->large; l; l = l->next) {              ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);                            if (l->alloc) {                  ngx_free(l->alloc);              }          }                 #if (NGX_DEBUG)                    /**         * we could allocate the pool->log from this pool         * so we can not use this log while the free()ing the pool         */                    for (p = pool, n = pool->d.next; /** void */; p = n, n = n->d.next) {              ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,                  "free: %p, unused: %uz", p, p->d.end - p->d.last);                            if (n == NULL) {                  break;              }          }          //只有debug模式才会执行这个片段的代码,主要是log记录,用以跟踪函数销毁时日志记录。      #endif          //释放小内存块        for (p = pool, n = pool->d.next; /** void */; p = n, n = n->d.next) {              ngx_free(p);                            if (n == NULL) {                  break;              }          }      }  

   该函数将遍历内存池链表,释放大块内存(通过ngx_free),而没有提供对小块内存的释放。如果注册了clenup(也是一个链表结构),

   亦将遍历该cleanup链表结构依次调用clenup的handler清理和该内存相关联的的其它资源,也就是当我们从内存池中申请资源时,

   可能会附带一些其它的资源(比如打开的文件),这些资源的使用和申请的内存是绑定在一起的,所以在进行资源释放时,希望这些资

  源的释放能够和内存池释放时一起进行(通过handler()回调函数),避免了资源泄露和单独执行释放相关资源的麻烦。

总结起来 ngx_destroy_pool() 释放内存池,一共分三步
第一步、在释放前先对业务逻辑进行释放前的处理
    for (c = pool->cleanup; c; c = c->next) {
          ...
    }
第二步、释放large占用的内存(大内存块链表)
    for (l = pool->large; l; l = l->next) {
          ....
    }
第三步、释放所有的池子(即小内存块链表)
for (p = pool, n = pool->next; /* void */; p = n, n = n->next) {
       ...
  }

 B,内存申请和释放

 下面简单介绍有关内存申请函数。 

,void *ngx_palloc(ngx_pool_t *pool, size_t size); 代码如下:

void *ngx_palloc(ngx_pool_t *pool, size_t size){    u_char      *m;    ngx_pool_t  *p;    if (size <= pool->max) {//判断待分配内存与max值         p = pool->current; //小于max值,则从current节点开始遍历pool链表         do {            m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT); // 对齐内存指针,加快存取速度            if ((size_t) (p->d.end - m) >= size) { //找到合适内存块                p->d.last = m + size;//在该节点指向的内存块中分配size大小的内存                return m;            }//如果不满足,则查找下一个节点            p = p->d.next;        } while (p);//链表里没有能分配size大小内存的节点,则生成一个新的节点并在其中分配内存          return ngx_palloc_block(pool, size);    }//大于max值,则在large链表里分配内存    return ngx_palloc_large(pool, size);}

  ngx_palloc(pool, size); 从这个pool中分配一块为size大小的内存。注意,此函数分配的内存的起始地址按照NGX_ALIGNMENT进行了对齐。

关于内存字节对齐,看下面的宏定义:

#define ngx_align(d, a)     (((d) + (a - 1)) & ~(a - 1))

这个宏使用来计算存储d字节的数据,如果按照a字节对齐,需要多少字节来存储对齐的数据,其中a为2的指数幂。

比如 ngx_align(9,4)  则其结果为12,因为按4字节对齐4的整数倍最小值 则为 12, 宏 ngx_align_ptr 和ngx_align同样的道理和作用。

对齐操作会提高系统处理的速度,上面已经解释过内存字节对齐的诸多好处,下面给出 ngx_palloc的流程图。

                                

界定小内存块和大内存块的值是 min (size, NGX_MAX_ALLOC_FROM_POOL (4095))。

,void *ngx_pnalloc(ngx_pool_t *pool, size_t size);

      从这个pool中分配一块为size大小的内存。ngx_pnalloc() 函数与ngx_palloc()函数唯一不同之处,就是在计算申请内存的指针的方式未按32位对齐方式计算。

,static void * ngx_palloc_block(ngx_pool_t *pool, size_t size);

       待申请新的内存池节点小于等于max是调用这个函数。

       ngx_palloc_block() 函数用来分配新的内存池块,形成一个链表。这个函数用static 修饰 可见其为内调函数,不会被外部调用。代码如下:

static void *ngx_palloc_block(ngx_pool_t *pool, size_t size){    u_char      *m;    size_t       psize;    ngx_pool_t  *p, *new, *current;    psize = (size_t) (pool->d.end - (u_char *) pool);//计算pool的大小    m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);//分配一块与pool大小相同的内存    if (m == NULL) {        return NULL;    }    new = (ngx_pool_t *) m;    new->d.end = m + psize;//设置end指针    new->d.next = NULL;    new->d.failed = 0;    m += sizeof(ngx_pool_data_t);//让m指向该块内存ngx_pool_data_t结构体之后数据区起始位置    m = ngx_align_ptr(m, NGX_ALIGNMENT);//按NGX_ALIGNMENT字节对齐    new->d.last = m + size;//在数据区分配size大小的内存并设置last指针      current = pool->current;//更新current    for (p = current; p->d.next; p = p->d.next) {        if (p->d.failed++ > 4) {//failed的值只在此处被修改            current = p->d.next;//失败6次以上移动current指针        }    }//将分配的block链入内存池     p->d.next = new;//尾插法//如果直到最后节点failed计数>=6次,则current指向新分配的内存池节点block    pool->current = current ? current : new;    return m;}
这个函数中申请到新的内存池块后,在该块中分配完ngx_pool_data_t结点后,将这个结点挂在内存池队列的结尾处。
 有两点 需要注意的:

1.新建的内存池节点的开头部分都只有结构体ngx_pool_data_t(注意不是40B的ngx_pool_t(创建内存池时才这样)而是16 B的ngx_pool_data_t,因为

内存池除头节点外的其它节点没有必要更多的ngx_pool_t描述结构,多么节省内存), 空闲内存的开始处new->d.last 不仅去除ngx_pool_data_t大小

的头结构体而且还需 要 ngx_align_ptr(m, NGX_ALIGNMENT)进行内存对齐。

2.ngx_pool_data_t结构中的failed及current的妙用。failed实际上是用来计数用的,current字段记录了后续从内存池分配内存的起始内存节点,即从

current指向 的内存池节点开始搜索可分配的内存,其中current的变动是根据统计来做的。如下
   for (p = current; p->d.next; p = p->d.next) {
    if (p->d.failed++ > 4) {
        current = p->d.next;
     }
 }
   当链表较长,由于内存池管理队列是单向链表, 所以每次从头到尾搜索是很费时的。每次搜寻失败的结点(非尾部结点)的failed加1。failed指出了

 该结点经历多少次查寻,如果从当前内存池节点分配内存总失败次数大于等于6次(由于p->d.failed初始值为0,至少失败6次才为真),就将current字段

 移动到下一个内存池节点,如下一个节点的failed次数也大于等于6次,再下一个。这样,下次再做类似查询时,可以跳过若干不必要的结点加快查询

速度。最后新申请的内存池节点采用尾插法插入内存池链表中。

,static void *ngx_palloc_large(ngx_pool_t *pool, size_t size);

      待申请新的内存池节点大于于max是调用这个函数。

  

//控制大块内存的申请static void *ngx_palloc_large(ngx_pool_t *pool, size_t size){    void              *p;    ngx_uint_t         n;    ngx_pool_large_t  *large;    p = ngx_alloc(size, pool->log);    if (p == NULL) {        return NULL;    }    n = 0;    for (large = pool->large; large; large = large->next) {        if (large->alloc == NULL) {            large->alloc = p;//把新分配的内存块设置在其空出的large的alloc指针字段下            return p;        }        if (n++ > 3) {//尝试5次仍未找到已释放大内存块后空出的ngx_pool_large_t头结构体            break;        }    }// 重新分配ngx_pool_large_t结构体    large = ngx_palloc(pool, sizeof(ngx_pool_large_t));    if (large == NULL) {        ngx_free(p);        return NULL;    }// 采用头插法插入新分配的大内存块    large->alloc = p;    large->next = pool->large;    pool->large = large;    return p;}

ngx_palloc_large() 函数专用来申请大块内存,其过程总结为如下两步:

第一步,调用ngx_alloc申请的大块内存。

第二步,在ngx_pool_t中大块内存节点large链表中寻找空闲的ngx_pool_larger结点。如果找到,将大块内存挂在该结点上。ngx_pool_larger队列中

查找空闲结点数不会超过五次。超过五个结点没找到空闲结点就放弃。如果超过5次仍没找到空闲的large节点,则创建一个新的ngx_pool_large_t结

构体,并将申请到大块内存挂在这个新结点上,最后将这个节点采用头插法插入链表头部。

综合 函数⑶、⑷ 可知 ngx_palloc_block,ngx_palloc_large 为nginx从系统申请新的内存池节点加入到ngx_pool_t这个内存池管理容器中。

,void *ngx_pcalloc(ngx_pool_t *pool, size_t size);

       该函数也是从ngx_pool_t内存池中分配size大小的内存,并且对分配的内存块进行了清零。内部实际上是调用ngx_palloc申请内存,然后调用

       ngx_memzero清零。

,void *ngx_prealloc(ngx_pool_t *pool, void *p, size_t old_size, size_t new_size);

        对指针p指向的一块内存再分配。如果p是NULL,则直接分配一块新的new_size大小的内存。如果p不是NULL, 新分配一块内存,并把旧内存

       中的内容拷贝 至新内存块中,然后释放p的旧内存(具体能不能释放旧的,要视具体的情况而定,这里不再详述)。这个函数实际上也是使用

        ngx_palloc实现的。

, void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);

         按照指定对齐大小alignment来申请一块大小为size的内存。此处获取的内存不管大小都将被置于大内存块链中管理。

(8),ngx_int_t  ngx_pfree(ngx_pool_t *pool, void *p);// 释放指定的内存

       对于被置于大块内存链,也就是被large字段管理的一列内存中的某块进行释放。该函数的实现是顺序遍历large管理的大块内存链表。所以效率

      比较低下。如果在这个链表中找到了这块内存,则释放,并返回NGX_OK。否则返回NGX_DECLINED。由于这个操作效率比较低下,除非必要,

     也就是说这块内存非 常大,确应及时释放,否则一般不需要调用。反正内存在这个pool被销毁的时候,总归会都释放掉的嘛!

     需要注意的是该函数只释放large链表中注册的内存,普通内存在ngx_destroy_pool中统一释放。

(9),ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);// 注册cleanup回叫函数(结构体)

        ngx_pool_t中的cleanup字段管理着一个特殊的链表,该链表的每一项都记录着一个特殊的需要释放的资源。对于这个链表中每个节点所包含的

      资源如何去释放, 是自说明的。这也就提供了非常大的灵活性。意味着,ngx_pool_t不仅仅可以管理内存,通过这个机制,也可以管理任何需要

      释放的资源,例如,关闭文件,或 者删除文件等等的。 (这个过程在nginx里面出现的比较多,也就是 xxxx_add 操作通常不是实际的添加操作,

     而是分配空间并返回一个指针,后续我们还要通过操作指针指向的空间来实现所谓的add)

下面介绍这个链表(在文件ngx_palloc.h定义):

typedef struct ngx_pool_cleanup_s  ngx_pool_cleanup_t;typedef void (*ngx_pool_cleanup_pt)(void *data);struct ngx_pool_cleanup_s {    ngx_pool_cleanup_pt   handler;// 是一个函数指针,指向一个可以释放data所对应资源的函数。该函数的只有一个参数,就是data    void                 *data;//指向要清除的数据      ngx_pool_cleanup_t   *next;//下一个cleanup callback};

看到这里,ngx_pool_cleanup_add这个函数的用法,我相信大家都应该有一些明白了。但是这个参数size是起什么作用的呢?这个 size就是要存储这个

data字段所指向 的资源的大小。比如我们需要最后删除一个文件。那我们在调用这个函数的时候,把size指定为存储文件名的字符串的大小,然后调用

这个函数给cleanup链表中增加一项。 该函数会返回新添加的这个节点。我们然后把这个节点中的data字段拷贝为文件名。把hander字段赋值为一个删

除文件的函数 (当然该函数的原型为:void(*ngx_pool_cleanup_pt)(void *data))。

Nginx中预定义了两个cleanup函数。

void ngx_pool_cleanup_file(void *data) 用来关闭打开的文件。

void ngx_pool_delete_file(void *data) 用来删除文件并且试图关闭文件。

概括起来如下图:

                   

由图可知,每个需要清理的资源都对应有一个头部结构,这个结构中有一个关键的字段handler,handler是一个函数指针,在挂载一个资源到内存池上的

时候,同时也会注册一个清理资源的函数到这个handler上。即是说,内存池在清理cleanup的时候,就是调用这个handler来清理对应的资源。  

4,下面是内存操作的一些例子

毫无疑问内存池的使用给nginx带来很大好处,比如内存使用便利、逻辑代码的简化、程序性能的提升等。 

为了 更好的理解 nginx 内存管理相关 的设计 和 使用 方法,下面我们写一个测试例子 进行 编译调试:

代码如下:

//ngx_pool_test.c #include <stdio.h>#include <string.h>#include "ngx_config.h"//包含相关 nginx 头文件#include "nginx.h"#include "ngx_conf_file.h"#include "ngx_core.h"#include "ngx_string.h"#include "ngx_palloc.h"volatile ngx_cycle_t *ngx_cycle; // 测试需要void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log,ngx_err_t err, const char *fmt, ...){}// 自定义结构体类型typedef struct demo_s {int key;char *name;}demo_t;//输出内存池相关信息:小块内存链表上个内存节点信息void pool_blocks(ngx_pool_t *pool){int n = 0;ngx_pool_t *pool_head = pool;//记录内存池头(链表)部节点        // ngxin 对于一个内存池(链表) 只有头节点 包含 max,current等信息         while(pool){printf("Block %d:\n", n+1);printf("block addr = 0x%0x\n", pool);printf("  .current = 0x%0x\n", pool_head->current); printf("unused memory size is %d\n", (ngx_uint_t)(pool->d.end - pool->d.last));printf("Block %d failed %d\n", n+1, pool->d.failed);pool = pool->d.next;++n;}printf("-------------------------------\n");}int main(){ngx_pool_t *pool;demo_t *demo;char name[] = "hello NGX!"; char *buf;       // 创建一个新的内存池        pool = ngx_create_pool(1024, NULL);printf("pool max is %d\n\n", pool->max);pool_blocks(pool);        // 向NGX申请内存demo = ngx_palloc(pool, sizeof(demo_t));buf = ngx_palloc(pool, strlen(name)+1);demo->key = 1;demo->name = buf;strcpy(buf, name);        // 输出数据        printf("Data\n");    printf("demo->key=%d, demo->name=%s\n", demo->key, demo->name);pool_blocks(pool);               // 申请内存ngx_palloc(pool, 970);pool_blocks(pool);ngx_palloc(pool, 970);pool_blocks(pool);ngx_destroy_pool(pool);return 0;}

上面的代码注意添加  ngx_cycle_t *ngx_cycle , ngx_log_error_core的相关声明,不然会出现如下错误:

undefined reference to `ngx_cycle'
undefined reference to `ngx_log_error_core'
对于上面的代码, 编写 相应的Makefile(不熟悉make的可以 参考这里)文件如下:

CC=gccC_FLAGS = -g -Wall -Wextra  DIR=/home/dane/nginx-1.2.0TARGETS=ngx_pool_testTARGETS_FILE=$(TARGETS).call:$(TARGETS)clean:rm -f $(TARGETS) *.oCORE_INCS=-I $(DIR)/src/core/ \  -I $(DIR)/objs/ \  -I $(DIR)/src/event \  -I $(DIR)/src/event/modules \  -I $(DIR)/src/os/unix \  -I $(DIR)/Nginx_Pre/pcre-8.32/NGX_OBJ = $(DIR)/objs/src/core/ngx_palloc.o \  $(DIR)/objs/src/core/ngx_string.o \  $(DIR)/objs/src/os/unix/ngx_alloc.o$(TARGETS):$(TARGETS_FILE)$(CC) $(C_FLAGS) $(TARGETS_FILE) $(CORE_INCS) $(NGX_OBJ) -o $@

makefile 文件 需要指定 相应的 依赖文件 包含文件 路径等。

上面的Makefile 编写好后, 直接 make 就可产生 出 可执行文件 ngx_pool_test

./ngx_pool_test 即可运行 可执行文件。

结果如下:

              

 由程序及上图运行结果可知,程序开始调用ngx_create_pool 创建大小为 1024大小的内存池头部节点,因为ngx_pool_t 的大小为40字节,

所以此时可用最大内存 pool->max 大小为 1024-40 为 984字节。随后调用函数ngx_palloc 在内存池上分配 sizeof(demo_t)大小的内存,并

申请其第二个成员对应的内存char* name,其总共申请内存大小为 8+11 为19字节,因此 第一个内存块所剩内存大小为 965字节。随后 我们

申请 970字节的内存 此时内存池中没有满足要求的内存块,因此内存池调用函数ngx_palloc_block向系统申请 size即1024字节大小的内存,并

挂接在内存池之上,此时新申请的内存池节点的头部只是占用ngx_pool_data_t (16字节)结构体大小的内存,所以可用内存大小为 1024-16 为

1008 字节,所以对于第二个block 去除 970字节所申请内存大小 还剩 38字节,随后的程序读者可以 根据程序 自行分析。


参考资料:

http://blog.csdn.net/livelylittlefish/article/details/6586946

http://code.google.com/p/nginxsrp/wiki/NginxCodeReview

http://www.alidata.org/archives/1390


原创粉丝点击