首頁  >  文章  >  後端開發  >  Nginx高階資料結構源碼分析(四)-----記憶體池

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

WBOY
WBOY原創
2016-07-30 13:30:16986瀏覽

記憶體池的使用為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;  
}  

大塊記憶體分配

rr

釋放記憶體:

//分配内存(地址对齐)  
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);    //大内存  
}  

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。

以上就介紹了Nginx高階資料結構原始碼分析(四)-----記憶體池,包含了方面的內容,希望對PHP教學有興趣的朋友有所幫助。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn