成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

分析Nginx 源碼 - ngx_palloc文件總結(jié)

Steve_Wang_ / 1011人閱讀

摘要:關(guān)于是自身實(shí)現(xiàn)的一個(gè)內(nèi)存池模塊,其遍及整個(gè)的源碼之中,也是能簡(jiǎn)潔高效處理各個(gè)請(qǐng)求的基礎(chǔ)所在。它本身是一個(gè)記錄表,其中記錄了整個(gè)內(nèi)內(nèi)存池的內(nèi)存分配信息鏈的頭指針。

關(guān)于

palloc是nginx自身實(shí)現(xiàn)的一個(gè)內(nèi)存池模塊,其遍及整個(gè)nginx的源碼之中,也是nginx能簡(jiǎn)潔高效處理各個(gè)請(qǐng)求的基礎(chǔ)所在。本文先從ngx_allocngx_palloc2個(gè)文件來(lái)解讀內(nèi)存模塊。

ngx_alloc文件

整個(gè)ngx_alloc包含了3個(gè)函數(shù):ngx_alloc、ngx_callocngx_memalign。
其中ngx_allocngx_calloc方法都是利用malloc方法來(lái)分配內(nèi)存,不同的是ngx_calloc方法會(huì)在分配后進(jìn)行初始化工作。
ngx_memalign方法,則是利用memalignposix_memalign方法申請(qǐng)一個(gè)內(nèi)存對(duì)齊的內(nèi)存塊。

內(nèi)存對(duì)齊的用處首先是可以提高cpu效率,因?yàn)椴粚?duì)齊會(huì)導(dǎo)致cpu訪問(wèn)內(nèi)存時(shí)候需要拆分內(nèi)存塊;第二是方便平臺(tái)的移植。

ngx_palloc模塊結(jié)構(gòu)體

上節(jié)的ngx_alloc文件是對(duì)c語(yǔ)言內(nèi)存的封裝,此后的內(nèi)存分配都是通過(guò)調(diào)取其中的三個(gè)方法進(jìn)行的。那么我們先來(lái)了解一下ngx_palloc包含的結(jié)構(gòu)體。

ngx_pool_s結(jié)構(gòu)體
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;
};

ngx_pool_s結(jié)構(gòu)體是整個(gè)內(nèi)存池的核心結(jié)構(gòu)體。它本身是一個(gè)記錄表,其中記錄了整個(gè)內(nèi)內(nèi)存池的內(nèi)存分配信息鏈的頭指針。其中主要的屬性分別是d、largecleanup三個(gè)屬性,這也是我們接下來(lái)要了解的三個(gè)結(jié)構(gòu)體的指針。

ngx_pool_data_t結(jié)構(gòu)體
typedef struct {
    u_char               *last;
    u_char               *end;
    ngx_pool_t           *next;
    ngx_uint_t            failed;
} ngx_pool_data_t;

ngx_pool_data_t結(jié)構(gòu)體其實(shí)就像是ngx_pool_s結(jié)構(gòu)體的一個(gè)詳細(xì)描述,其中描述了一個(gè)內(nèi)存池的信息,包括當(dāng)前分配完的內(nèi)存地址、內(nèi)存池最后的內(nèi)存地址、下一個(gè)內(nèi)存池指針以及分配內(nèi)存失敗次數(shù)。

ngx_pool_large_s結(jié)構(gòu)體
struct ngx_pool_large_s {
    ngx_pool_large_t     *next;
    void                 *alloc;
};

這個(gè)結(jié)構(gòu)體就比較簡(jiǎn)單,就算一個(gè)鏈表,并包含一個(gè)指針指向當(dāng)前分配的內(nèi)存塊。

ngx_pool_cleanup_s結(jié)構(gòu)體
struct ngx_pool_cleanup_s {
    ngx_pool_cleanup_pt   handler;
    void                 *data;
    ngx_pool_cleanup_t   *next;
};

ngx_pool_cleanup_s結(jié)構(gòu)體的功能主要是用來(lái)在銷毀內(nèi)存池時(shí),需要處理一下其他的操作來(lái)保證內(nèi)存的正常銷毀,避免內(nèi)存的泄露。因此,在銷毀內(nèi)存期間,會(huì)觸發(fā)這個(gè)ngx_pool_cleanup_s的鏈表,并以此執(zhí)行銷毀函數(shù)。

ngx_pool_cleanup_file_t結(jié)構(gòu)體
typedef struct {
    ngx_fd_t              fd;
    u_char               *name;
    ngx_log_t            *log;
} ngx_pool_cleanup_file_t;

這個(gè)結(jié)構(gòu)體,主要用途就是為了在銷毀內(nèi)存塊的時(shí)候,能對(duì)文件描述符進(jìn)行關(guān)閉等操作。(感覺(jué)是這樣)

ngx_palloc模塊函數(shù) ngx_create_pool方法
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);
    p->d.end = (u_char *) p + size;
    p->d.next = 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;
}

這個(gè)方法主要是利用ngx_memalign方法來(lái)分配內(nèi)存塊,然后計(jì)算出d.lastd.end的2個(gè)屬性,其他屬性都比較容易理解。

ngx_palloc方法以及ngx_pnalloc方法
void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
    if (size <= pool->max) {
        return ngx_palloc_small(pool, size, 1);
    }
#endif

    return ngx_palloc_large(pool, size);
}

該函數(shù)理解比較簡(jiǎn)單,就算判斷內(nèi)存塊大小是否大于最大的內(nèi)存塊,若大于則使用大塊內(nèi)存的分配。

ngx_palloc_small方法
static ngx_inline void *
ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)
{
    u_char      *m;
    ngx_pool_t  *p;

    p = pool->current;

    do {
        m = p->d.last;

        if (align) {
            m = ngx_align_ptr(m, NGX_ALIGNMENT);
        }

        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);
}

在分配小塊內(nèi)存時(shí),就算不斷的尋找是否存在符合條件的內(nèi)存大小,若存在,則將內(nèi)存塊地址返回,并將d.last往后移動(dòng)分配的內(nèi)存大小,即完成了內(nèi)存分配。若不存在,則利用ngx_palloc_block方法去生成一個(gè)新的內(nèi)存塊。

ngx_palloc_block方法
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);
    m = ngx_align_ptr(m, NGX_ALIGNMENT);
    new->d.last = m + size;

    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;
}

該函數(shù)其實(shí)用途在于重新生成一個(gè)新的內(nèi)存池,同時(shí)內(nèi)存池的大小和最初的內(nèi)存池是相同大小。關(guān)鍵在于,他會(huì)對(duì)失敗大于4次的內(nèi)存池的當(dāng)前指針進(jìn)行移動(dòng),這樣可以提高之后的內(nèi)存查找的效率。

ngx_palloc_large方法
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;
            return p;
        }

        if (n++ > 3) {
            break;
        }
    }

    large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
    if (large == NULL) {
        ngx_free(p);
        return NULL;
    }

    large->alloc = p;
    large->next = pool->large;
    pool->large = large;

    return p;
}

nginx內(nèi)存池有趣的地方就在于,他們直接可能會(huì)互相調(diào)用來(lái)實(shí)現(xiàn)自己的功能,例如當(dāng)前的方法,首先它回去直接申請(qǐng)一個(gè)需要的內(nèi)存塊,之后它需要去查找ngx_pool_large_t的鏈表,看看有沒(méi)有某個(gè)ngx_pool_large_talloc是為空的,這樣就可以將分配好的地址掛載上去。
若不存在,那么就利用small方法申請(qǐng)一個(gè)ngx_pool_large_t的節(jié)點(diǎn),然后將其加入ngx_pool_large_t的鏈表中。

ngx_pmemalign方法
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_small(pool, sizeof(ngx_pool_large_t), 1);
    if (large == NULL) {
        ngx_free(p);
        return NULL;
    }

    large->alloc = p;
    large->next = pool->large;
    pool->large = large;

    return p;
}

該方法就是ngx_palloc_large簡(jiǎn)單暴力版,直接申請(qǐng)ngx_pool_large_t并加入鏈表中。

ngx_destroy_pool方法
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) {
        if (c->handler) {
            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
                           "run cleanup: %p", c);
            c->handler(c->data);
        }
    }

#if (NGX_DEBUG)
    ... ...
#endif

    for (l = pool->large; l; l = l->next) {
        if (l->alloc) {
            ngx_free(l->alloc);
        }
    }

    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
        ngx_free(p);

        if (n == NULL) {
            break;
        }
    }
}

ngx_destroy_pool方法的執(zhí)行流程主要如下:先進(jìn)行cleanup操作,觸發(fā)銷毀方法、再進(jìn)行大塊內(nèi)存的銷毀、最后銷毀銷毀內(nèi)存。銷毀方法都是使用ngx_free,其實(shí)就算free方法。

ngx_pool_cleanup_add方法
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));
    if (c == NULL) {
        return NULL;
    }

    if (size) {
        c->data = ngx_palloc(p, size);
        if (c->data == NULL) {
            return NULL;
        }

    } else {
        c->data = NULL;
    }

    c->handler = NULL;
    c->next = p->cleanup;

    p->cleanup = c;

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);

    return c;
}

該方法主要是為內(nèi)存池添加一個(gè)銷毀的接口對(duì)象,先進(jìn)行分配內(nèi)存塊,之后再在該內(nèi)存上初始化變量,變量類似ngx_pool_cleanup_file_t,然后設(shè)置handle屬性,用于以后內(nèi)存池銷毀。

總結(jié)

nginx的內(nèi)存池功能相對(duì)stl的內(nèi)存池更好理解,也許是代碼風(fēng)格問(wèn)題導(dǎo)致閱讀難度的增加。不過(guò)學(xué)習(xí)了nginx的內(nèi)存分配后,就可以開(kāi)始其他的模塊的閱讀。

相關(guān)鏈接

為什么要進(jìn)行內(nèi)存對(duì)齊以及對(duì)齊規(guī)則

nginx源碼分析—內(nèi)存池結(jié)構(gòu)ngx_pool_t及內(nèi)存管理

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/39649.html

相關(guān)文章

  • Nginx 源碼分析:ngx_list_t

    摘要:源碼路徑版本主要作用分析是對(duì)通常的這種數(shù)據(jù)結(jié)構(gòu)重復(fù)的造輪子。鏈表使用的內(nèi)存池。在堆上創(chuàng)建調(diào)用函數(shù)與的分析類似,調(diào)用該函數(shù)會(huì)自動(dòng)向申請(qǐng)內(nèi)存空間。 源碼路徑 版本:1.8.0 srccoreNgx_list.h srccoreNgx_list.c 主要作用分析 ngx_list_t是Nginx對(duì)通常的list這種數(shù)據(jù)結(jié)構(gòu)重復(fù)的造輪子。 在本篇中,我們先來(lái)分析Nginx是如何造這...

    Kahn 評(píng)論0 收藏0
  • Nginx 源碼分析:ngx_pool_t

    摘要:源代碼路徑版本主要作用分析提供了一種機(jī)制,幫助進(jìn)行資源管理內(nèi)存文件。用來(lái)標(biāo)記該使用時(shí)分配失敗次數(shù)。根據(jù)以上思路,可以很容易明白源碼里關(guān)于創(chuàng)建鏈表的代碼函數(shù)聲明說(shuō)明輸入要分配的節(jié)點(diǎn)大小,返回一個(gè)的指針。 源代碼路徑 版本:1.8.0 srccoreNgx_palloc.h srccoreNgx_palloc.c 主要作用分析 提供了一種機(jī)制,幫助進(jìn)行資源管理(內(nèi)存、文件)。可以...

    codergarden 評(píng)論0 收藏0
  • Nginx源碼分析Nginx的內(nèi)存管理

    摘要:而對(duì)于堆內(nèi)存,通常需要程序員進(jìn)行管理。我們通常說(shuō)的內(nèi)存管理亦是只堆空間內(nèi)存管理。內(nèi)存管理整體可以分為個(gè)部分,第一部分是常規(guī)的內(nèi)存池,用于進(jìn)程平時(shí)所需的內(nèi)存管理第二部分是共享內(nèi)存的管理。將內(nèi)存塊按照的整數(shù)次冪進(jìn)行劃分最小為最大為。 施洪寶 一. 概述 應(yīng)用程序的內(nèi)存可以簡(jiǎn)單分為堆內(nèi)存,棧內(nèi)存。對(duì)于棧內(nèi)存而言,在函數(shù)編譯時(shí),編譯器會(huì)插入移動(dòng)棧當(dāng)前指針位置的代碼,實(shí)現(xiàn)??臻g的自管理。而對(duì)于...

    raise_yang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<