nginx 源碼學習筆記(七)

內存分配相關

1.      系統功能封裝

內存相關的操作主要在os/unix/ngx_alloc.{h,c} 和 core/ngx_palloc.{h,c}中。

其中os/unix/ngx_alloc.{h,c}封裝了最基本的內存分配函數,是對c原有的malloc/free/memalign等函數的封裝,對應函數爲:

a.ngx_alloc:對malloc進行了簡單的封裝;

    void *  
    ngx_alloc(size_t size, ngx_log_t *log)  
    {  
        void  *p;  
        p = malloc(size);  
        if (p == NULL) {  
            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,  
                          "malloc(%uz) failed", size);    }  
        ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size);  
        return p;  
    }  

b.ngx_calloc:使用ngx_alloc分配內存,並且把內存賦值0:

    void *  
    ngx_calloc(size_t size, ngx_log_t *log)  
    {  
        void  *p;  
        p = ngx_alloc(size, log);  
        if (p) {     ngx_memzero(p, size);    }//在core/ngx_string.h中定義  
    // #define ngx_memzero(buf, n)       (void) memset(buf, 0, n) 初始化爲0  
        return p;  
    }  

c. ngx_memalign  返回基於一個指定的alignment大小的數值爲對齊基數的空間

d.ngx_free    內存釋放操作


2.      nginx內存池

爲了方便系統模塊對內存的使用,方便內存的管理,nginx自己施行了進程池機制來進行內存的分配和釋放,首先nginx會在特定的生命週期幫你統一建立內存池,當需要進行內存分配的時候同一通過內存池中的內存進行分配,最後nginx會在適當的時候釋放內存吃的資源,開發者只要在需要的時候對內存進行申請即可,不用過多考慮釋放的問題,這也就是在os/unix/ngx_alloc.c文件中沒有看到free操作的原因吧。

 

下面來看一下內存池的主要結構:

    <span style="font-size:16px;">ngx_palloc.h  
    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;  
    };  
    typedef struct {  
        u_char               *last;  
        u_char               *end;  
        ngx_pool_t           *next;  
        ngx_uint_t            failed;  
    } ngx_pool_data_t;  
      
    ngx_core.h  
    typedef struct ngx_pool_s        ngx_pool_t;  
    typedef struct ngx_chain_s       ngx_chain_t;  
    </span>  




    <span style="font-size:16px;">src/core/ngx_palloc.c  
    //創建內存池  
    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);  //創建對其空間  
        if (p == NULL) {  
            return NULL;  
        }  
      
        p->d.last = (u_char *) p + sizeof(ngx_pool_t);    //初始指向ngx_pool_t結構體後面  
        p->d.end = (u_char *) p + size;                   //整個結構體的結尾  
        p->d.next = NULL;                                 //沒有next  
        p->d.failed = 0;  
      
        size = size - sizeof(ngx_pool_t);    //剩餘大小  
    p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;//最大不超過NGX_MAX_ALLOC_FROM_POOL  
    //#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)  
      
        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;  
      
        for (c = pool->cleanup; c; c = c->next) {//如果註冊了clenup(一種鏈表結構),會依次調用clenup的handler進行清理  
            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) { //遍歷鏈表,釋放所有large內存  
      
            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);  
      
            if (l->alloc) {  
                ngx_free(l->alloc);  
            }  
        }  
      
    #if (NGX_DEBUG)  //等譯debug級別,如果爲true,會打印日誌  
      
        /* 
         * 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  
      
        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) { //釋放掉所有large段內存  
            if (l->alloc) {  
                ngx_free(l->alloc);  
            }  
        }  
      
        pool->large = NULL;  
      
        for (p = pool; p; p = p->d.next) {  
            p->d.last = (u_char *) p + sizeof(ngx_pool_t);將指針重新指向ngx_pool_t(和創建時一樣)  
        }  
    }  
      
    //從內存池裏分配內存  
    void * ngx_palloc(ngx_pool_t *pool, size_t size)  
    void * ngx_pnalloc(ngx_pool_t *pool, size_t size)  
    void * ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)  
    void * ngx_pcalloc(ngx_pool_t *pool, size_t size)  
      
    這裏以ngx_palloc爲例講解,其他大同小異:  
    void *  
    ngx_palloc(ngx_pool_t *pool, size_t size)  
    {  
        u_char      *m;  
        ngx_pool_t  *p;  
        if (size <= pool->max) { //判斷分配內存是否大於pool->max,如果小於等於  
            p = pool->current; //嘗試從鏈表的current開始遍歷,  
            do {  
                m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);  
    //#define ngx_align_ptr(p,a)    
    //(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))  
      
                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);//如果無法分配內存,就生成一個新的節點,同時pool->current指針指向新的位置  
        }  
        return ngx_palloc_large(pool, size);  //如果分配的內存大於pool->max則在large鏈表分配一段內存  
    }  
      
    //釋放指定的內存  
    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) {        //存在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;  
    }//由代碼可以看出,這個操作只有在內存large鏈表裏面註冊內存纔會真正釋放,如果分配的是普通的內存,則會在destory_pool的時候統一釋放。  
      
    //註冊cleanup回調函數  
    ngx_pool_cleanup_t *  
    ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)  
    {  
        ngx_pool_cleanup_t  *c;  
        c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));  //分配cleanup空間  
        if (c == NULL) {  
            return NULL;  
        }  
        if (size) {  
            c->data = ngx_palloc(p, size);         //爲cleanup結構體分配data空間  
            if (c->data == NULL) {  
                return NULL;  
            }  
        } else {  
            c->data = NULL;  
        }  
        c->handler = NULL;  
        c->next = p->cleanup;  
        p->cleanup = c;               // 增加cleanup  
        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);  
        return c;                    //返回結構體分配的空間  
    }</span>  


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章