Nginx進階資料結構源碼分析(四)-----記憶體池

來源:互聯網
上載者:User
記憶體池的使用給Nginx帶來了很多好處,比如記憶體使用量的便利,邏輯代碼的簡化以及程式效能的提升。

幾個關鍵知識點羅列如下:

(1)函數ngx_palloc()嘗試從記憶體中分配size大小的記憶體時,分兩種情況,一種是size大小小於pool->max,稱為小塊記憶體配置,若當前記憶體池節點小於size,則申請一個新的等同大小的記憶體池節點,然後從這個新記憶體池節點分配出size大小的記憶體空間。若size 大於pool->max時,即分配大塊記憶體,此時調用的函數直接向作業系統申請大塊記憶體。

(2)小塊記憶體的申請是插入在鏈表的尾節點,而新的大塊記憶體則是插入在鏈表前面。

(3)Nginx僅提供對大塊記憶體的釋放,沒有提供對小塊記憶體的釋放,意味著從記憶體池分配出去的記憶體不會再回收到記憶體池裡來,而只有在銷毀整個記憶體池時,這些記憶體才會回收到系統記憶體裡。

(4)ngx_pool_t中的current欄位:這個欄位記錄了後序從該記憶體池分配記憶體的起始節點,Nginx規定當一個記憶體節點分配總失敗次數大於等於6次時,current則指向下一個記憶體節點。

(5)為什麼要將pool->max欄位的最大值限制在一頁記憶體,這也是小塊記憶體與大塊記憶體的臨界,原因在於只有當分配的空間小於一頁時才有緩衝的必要,,否則的話還不如直接利用系統介面malloc()向作業系統申請。

各個結構體的定義:

//大塊記憶體管理結構  struct ngx_pool_large_s {      ngx_pool_large_t     *next;   //串連下一個大記憶體管理      void                 *alloc;  //申請的大記憶體位址  };    //記憶體池中資料管理  typedef struct {      u_char               *last;   //可用記憶體的起始地址      u_char               *end;    //可用記憶體的末尾地址      ngx_pool_t           *next;   //指向下一個記憶體池節點     ngx_uint_t            failed; //申請時,失敗的次數  } ngx_pool_data_t;    //記憶體池  struct ngx_pool_s {      ngx_pool_data_t       d;       //存放資料      size_t                max;      //存放資料的可用記憶體大小,最大為1頁      ngx_pool_t           *current; //指向分配記憶體的記憶體池      ngx_chain_t          *chain;      ngx_pool_large_t     *large;   //串連大記憶體管理結構      ngx_pool_cleanup_t   *cleanup;  //清理對象頭      ngx_log_t            *log;  };  
記憶體池的初始化:

//建立一個size的記憶體池  ngx_pool_t *  ngx_create_pool(size_t size, ngx_log_t *log)  {      ngx_pool_t  *p;        p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);  //以對齊的方式來申請size位元組記憶體      if (p == NULL) {          return NULL;      }        p->d.last = (u_char *) p + sizeof(ngx_pool_t);   //指向可用的記憶體起始地址      p->d.end = (u_char *) p + size;                  //指向可用記憶體的末尾地址      p->d.next = NULL;                                //初始時,下一個可用記憶體為NULL      p->d.failed = 0;                             //該記憶體申請失敗零次        size = size - sizeof(ngx_pool_t);       //實際可用的大小,減去控制結構的大小      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;  }  
記憶體池的銷毀與重設:

//銷毀記憶體池  void  ngx_destroy_pool(ngx_pool_t *pool)  {      ngx_pool_t          *p, *n;      ngx_pool_large_t    *l;      ngx_pool_cleanup_t  *c;        //運行清理對象的handler      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);          }      }        //釋放大記憶體      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);      //使用free釋放malloc申請的記憶體          }      }    #if (NGX_DEBUG)        /*      * we could allocate the pool->log from this pool      * so we cannot use this log while 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;          }      }    #endif        //釋放每一個申請的記憶體池對象ngx_pool_t      for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {          ngx_free(p);            if (n == NULL) {              break;          }      }  }    //重設記憶體池  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);          }      }        //記憶體池對象,僅僅改變last的指標位置      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;  }  
分配記憶體:

//分配記憶體(地址對齊)  void *  ngx_palloc(ngx_pool_t *pool, size_t size)  {      u_char      *m;      ngx_pool_t  *p;        if (size <= pool->max) {  //小記憶體申請時,以size為標準            p = pool->current;            do {              m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT); //首先將d.last地址對齊                if ((size_t) (p->d.end - m) >= size) {        //可用的記憶體大於要申請的記憶體                  p->d.last = m + size;        //直接更新d.last                    return m;       //直接返回              }                p = p->d.next;       //否則找下一個可用的記憶體池對象            } while (p);            //沒有找到,則要申請新的記憶體池對象          return ngx_palloc_block(pool, size);      }        return ngx_palloc_large(pool, size);    //大記憶體申請處理  }    //分配記憶體(地址可以不對齊)  void *  ngx_pnalloc(ngx_pool_t *pool, size_t size)  {      u_char      *m;      ngx_pool_t  *p;        if (size <= pool->max) {  //小記憶體            p = pool->current;            do {              m = p->d.last;                if ((size_t) (p->d.end - m) >= size) {                  p->d.last = m + size;                    return m;              }                p = p->d.next;            } while (p);            return ngx_palloc_block(pool, size);    //申請新記憶體池對象      }        return ngx_palloc_large(pool, size);    //大記憶體  }  
小塊記憶體配置:

//申請新的記憶體池對象  static void *  ngx_palloc_block(ngx_pool_t *pool, size_t size)  {      u_char      *m;      size_t       psize;      ngx_pool_t  *p, *new;        psize = (size_t) (pool->d.end - (u_char *) pool);    //申請記憶體的總大小        m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);  //對齊申請記憶體      if (m == NULL) {          return NULL;      }        new = (ngx_pool_t *) m; //新的記憶體        new->d.end = m + psize;      //可用的記憶體的最後地址      new->d.next = NULL;      new->d.failed = 0;        m += sizeof(ngx_pool_data_t);   //只有一個ngx_pool_data_t,節省了ngx_pool_t的其餘開銷      m = ngx_align_ptr(m, NGX_ALIGNMENT);      new->d.last = m + size;      //可用的記憶體的起始地址            //如果進行中的申請記憶體的失敗的次數已經有5次了,第6次,current將會指向新的記憶體池對象      for (p = pool->current; p->d.next; p = p->d.next) {          if (p->d.failed++ > 4) {              pool->current = p->d.next;          }      }        p->d.next = new; //串連剛剛申請的記憶體池對象        return m;  }  
大塊記憶體配置

//大記憶體申請處理  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);      //直接malloc申請記憶體      if (p == NULL) {          return NULL;      }        n = 0;        for (large = pool->large; large; large = large->next) {          if (large->alloc == NULL) {      //如果有記憶體被釋放了,可重用              large->alloc = p;                  return p;          }            if (n++ > 3) {       //但是只找4次,第5次直接break,建立大記憶體的管理結構              break;          }      }        large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); //從記憶體池對象申請記憶體      if (large == NULL) {          ngx_free(p);          return NULL;      }        large->alloc = p;    //指向申請的大記憶體        //插入large的頭      large->next = pool->large;          pool->large = large;        return p;  }  
直接分配記憶體:

//不管記憶體大小多大,向作業系統申請記憶體  void *  ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)  {      void              *p;      ngx_pool_large_t  *large;        p = ngx_memalign(alignment, size, pool->log);    //申請的記憶體      if (p == NULL) {          return NULL;      }        large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); //申請一個大記憶體管理結構      if (large == NULL) {          ngx_free(p);          return NULL;      }        //放入到記憶體池ngx_pool_t中管理      large->alloc = p;    //指向申請的記憶體        //插入到頭部      large->next = pool->large;      pool->large = large;        return p;  }  
釋放記憶體:

//釋放記憶體  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;  }  

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

以上就介紹了Nginx進階資料結構源碼分析(四)-----記憶體池,包括了方面的內容,希望對PHP教程有興趣的朋友有所協助。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.