天天看點

nginx中的記憶體池 http://www.cnblogs.com/sld666666/archive/2010/06/27/1766255.html 1.為什麼需要記憶體池 2.記憶體池的原理 3.nginx中的記憶體池

    為什麼需要記憶體池?

a. 在大量的小塊記憶體的申請和釋放的時候,能更快地進行記憶體配置設定(對比malloc和free)

b.減少記憶體碎片,防止記憶體洩露。

    記憶體池的原理非常簡單,用申請一塊較大的記憶體來代替N多的小記憶體塊,當有需要malloc一塊

比較小的記憶體是,直接拿這塊大的記憶體中的位址來用即可。

    當然,這樣處理的缺點也是很明顯的,申請一塊大的記憶體必然會導緻記憶體空間的浪費,但是

比起頻繁地malloc和free,這樣做的代價是非常小的,這是典型的以空間換時間。

    一個典型的記憶體池如下圖所示:

<a href="http://images.cnblogs.com/cnblogs_com/sld666666/WindowsLiveWriter/nginx3nginx_112BD/MemoryPool_Step5_2.png"></a>

                 圖一:一個典型的記憶體池。

    首先定義這樣一個結構體:

    一個記憶體池就是這樣一連串的記憶體塊組成。當需要用到記憶體的時候,調用此記憶體池定義好的接口

GetMemory(),而需要删除的時候FreeMemory()。

    而GetMemory和FreeMemory幹了什麼呢?GetMemory隻是簡單傳回記憶體池中可用空間的位址。

而FreeMemory幹了兩件事情:一: 改變UsedSize 的值,二:重新初始化這一記憶體區域。

    首先我們看一下nginx記憶體池的定義:

代碼

<a href="http://www.cnblogs.com/sld666666/archive/2010/06/27/1766255.html"></a>

struct ngx_pool_s {

ngx_pool_data_t d;//表示資料區域

size_t max;//記憶體池能容納資料的大小

ngx_pool_t * current;//目前記憶體池塊(nginx中的記憶體池是又一連串的記憶體池連結清單組成的)

ngx_chain_t* chain;//主要為了将記憶體池連接配接起來

ngx_pool_large_t* large;//大塊的資料

ngx_pool_cleanup_t* cleanup;//清理函數

ngx_log_t* log;//寫log

};

nginx中的記憶體池和普通的有比較大的不同。nginx中的記憶體池是由N個記憶體池連結清單

組成的,當一個記憶體池滿了以後,就會從下一個記憶體池中提取空間來使用。 

對于ngx_pool_data_t的定義非常簡單:

typedef struct {

u_char *last;

u_char *end;

ngx_pool_t *next;

ngx_uint_t failed;

} ngx_pool_data_t;

其中last表示目前資料區域的已經使用的資料的結尾。

end表示目前記憶體池的結尾。

next表示下一個記憶體池,前面已經說過,再nignx中,當一個記憶體池空間

不足的時候,它不會擴大其空間,而是再建立一個記憶體池,組成一個記憶體池連結清單。

failed标志申請記憶體的時候失敗的次數。

在了解了這個結構體後面的就非常簡單了。

current 表示目前的記憶體池。

chain表示記憶體池連結清單。

large表示大塊的資料。

對于ngx_pool_large_t定義如下:

struct ngx_pool_large_s {

ngx_pool_large_t* next;

void* alloc;

此結構體的定義也是非常簡單的。一個記憶體位址的指針已經指向下一個位址的指針。

這裡再解釋下為什麼需要有large資料塊。當一個申請的記憶體空間大小比記憶體池的大小還要大的時候,

malloc一塊大的空間,再記憶體池用保留這個位址的指針。

Cleanup保持存着記憶體池被銷毀的時候的清理函數。

typedef void (*ngx_pool_cleanup_pt)(void *data);

struct ngx_pool_cleanup_s {

ngx_pool_cleanup_pt handler;

void* data;

ngx_pool_cleanup_t* next;

ngx_pool_cleanup_pt 是一個函數指針的典型用法,

在這個結果中儲存這需要清理的資料指針以及相應的清理函數, 讓記憶體池銷毀

或其他需要清理記憶體池的時候,可以調用此結構體中的handler。

    下面是我畫的一張nginx的記憶體池的結構圖。

                                                        &lt;圖1. ngx_pool 結構體&gt;

    要大體了解一個記憶體池,隻需要了解其池子的建立,記憶體的配置設定以及池子的銷毀即可。下面就分析下

ngx_pool_t的這個3個方面。注:其中有些代碼可能與ngx_pool中的源代碼有所差異,但是整體意思

絕對是一樣的,本人的修改,隻是為了更好的分析,比如 我就把所有寫log的過程都去掉了。

建立一個記憶體池

    nginx記憶體池的建立非常簡單,申請一開size大小的記憶體,把它配置設定給 ngx_poo_t。

從記憶體池中配置設定記憶體.

    這個函數從記憶體池用拿出記憶體,如果目前記憶體池已滿,到下一個記憶體池,如果所有的記憶體池已滿,

增加一個新的記憶體池,如果申請的記憶體超過了記憶體池的最大值,從*large中配置設定

記憶體池的銷毀

    銷毀一個記憶體池其實就是幹了三件事, 調用清理韓式, 釋放大塊的記憶體,釋放記憶體池,需要注意的

一點是在nginx中, 小塊記憶體除了在記憶體池被銷毀的時候都是不能被釋放的。

    前面說過,在nginx中,當記憶體池滿了以後,會增加一個新的記憶體池。這個動作就是靠ngx_palloc_block

函數實作的。

    這個函數就是申請了一塊記憶體區域,變為一個記憶體池,然後把它連接配接到原來記憶體池的末尾。

    在nginx中,小塊記憶體除了在記憶體池銷毀之外是不能釋放的,但是大塊記憶體卻可以,這兩個

函數就是用來控制大塊記憶體的申請和釋放, 代碼也非常簡單,調用malloc申請記憶體,連接配接到

ngx_pool_large_t中 和 調用free釋放記憶體。這裡就不貼上代碼了。

繼續閱讀