記憶體池

記憶體池

(Memory Pool)是一種記憶體分配方式。通常我們習慣直接使用new、malloc等API申請分配記憶體,這樣做的缺點在於:由於所申請記憶體塊的大小不定,當頻繁使用時會造成大量的記憶體碎片並進而降低性能。

基本介紹

基本概念,實現示例,

基本概念

記憶體池則是在真正使用記憶體之前,先申請分配一定數量的、大小相等(一般情況下)的記憶體塊留作備用。當有新的記憶體需求時,就從記憶體池中分出一部分記憶體塊,若記憶體塊不夠再繼續申請新的記憶體。這樣做的一個顯著優點是,使得記憶體分配效率得到提升。
核心中有不少地方記憶體分配不允許失敗. 作為一個在這些情況下確保分配的方式, 核心開發者創建了一個已知為記憶體池(或者是 "mempool" )的抽象. 一個記憶體池真實地只是一類後備快取, 它盡力一直保持一個空閒記憶體列表給緊急時使用.
一個記憶體池有一個類型 mempool_t ( 在 <linux/mempool.h> 中定義); 你可以使用 mempool_create 創建一個:
mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data); min_nr 參數是記憶體池應當一直保留的最小數量的分配的對象. 實際的分配和釋放對象由 alloc_fn 和 free_fn 處理, 它們有這些原型:
typedef void *(mempool_alloc_t)(int gfp_mask, void *pool_data); typedef void (mempool_free_t)(void *element, void *pool_data); 給 mempool_create 最後的參數 ( pool_data ) 被傳遞給 alloc_fn 和 free_fn.
如果需要, 你可編寫特殊用途的函式來處理 mempool 的記憶體分配. 常常, 但是, 你只需要使核心 slab 分配器為你處理這個任務. 有 2 個函式 ( mempool_alloc_slab 和 mempool_free_slab) 來進行在記憶體池分配原型和 kmem_cache_alloc 和 kmem_cache_free 之間的感應淬火. 因此, 設定記憶體池的代碼常常看來如此:
cache = kmem_cache_create(. . .); pool = mempool_create(MY_POOL_MINIMUM,mempool_alloc_slab, mempool_free_slab, cache); 一旦已創建了記憶體池, 可以分配和釋放對象,使用:
void *mempool_alloc(mempool_t *pool, int gfp_mask); void mempool_free(void *element, mempool_t *pool); 當記憶體池創建了, 分配函式將被調用足夠的次數來創建一個預先分配的對象池. 因此, 對 mempool_alloc 的調用試圖從分配函式請求額外的對象; 如果那個分配失敗, 一個預先分配的對象(如果有剩下的)被返回. 當一個對象被用 mempool_free 釋放, 它保留在池中, 如果對齊預分配的對象數目小於最小量; 否則, 它將被返回給系統.
一個 mempool 可被重新定大小, 使用:
int mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask); 這個調用, 如果成功, 調整記憶體池的大小至少有 new_min_nr 個對象. 如果你不再需要一個記憶體池, 返回給系統使用:
void mempool_destroy(mempool_t *pool); 你編寫返回所有的分配的對象, 在銷毀 mempool 之前, 否則會產生一個核心 oops.
如果你考慮在你的驅動中使用一個 mempool, 請記住一件事: mempools 分配一塊記憶體在一個鍊表中, 對任何真實的使用是空閒和無用的. 容易使用 mempools 消耗大量的記憶體. 在幾乎每個情況下, 首選的可選項是不使用 mempool 並且代替以簡單處理分配失敗的可能性. 如果你的驅動有任何方法以不危害到系統完整性的方式來回響一個分配失敗, 就這樣做. 驅動代碼中的 mempools 的使用應當少.

實現示例

記憶體池的實現有很多,性能和適用性也不相同,以下是一種較簡單的C++實現 -- GenericMP模板類。(本例取材於《Online game server programming》一書)
在這個例子中,使用了模板以適應不同對象的記憶體需求,記憶體池中的記憶體塊則是以基於鍊表的結構進行組織。
GenericMP模板類定義
template <class T, int BLOCK_NUM= 50>
class GenericMP
{
public:
static VOID *operator new(size_t allocLen)
{
assert(sizeof(T) == allocLen);
if(!m_NewPointer)
MyAlloc();
UCHAR *rp = m_NewPointer;
m_NewPointer = *reinterpret_cast<UCHAR**>(rp); //由於頭4個位元組被“強行”解釋為指向下一記憶體塊的指針,這裡m_NewPointer就指向了下一個記憶體塊,以備下次分配使用。
return rp;
}
static VOID operator delete(VOID *dp)
{
*reinterpret_cast<UCHAR**>(dp) = m_NewPointer;
m_NewPointer = static_cast<UCHAR*>(dp);
}
private:
static VOID MyAlloc()
{
m_NewPointer = new UCHAR[sizeof(T) * BLOCK_NUM];
UCHAR **cur = reinterpret_cast<UCHAR**>(m_NewPointer); //強制轉型為雙指針,這將改變每個記憶體塊頭4個位元組的含義。
UCHAR *next = m_NewPointer;
for(INT i = 0; i < BLOCK_NUM-1; i++)
{
next += sizeof(T);
*cur = next;
cur = reinterpret_cast<UCHAR**>(next); //這樣,所分配的每個記憶體塊的頭4個位元組就被“強行“解釋為指向下一個記憶體塊的指針, 即形成了記憶體塊的鍊表結構。
}
*cur = 0;
}
static UCHAR *m_NewPointer;
protected:
~GenericMP()
{
}
};
template<class T, int BLOCK_NUM >
UCHAR *GenericMP<T, BLOCK_NUM >::m_NewPointer;
GenericMP模板類套用
class ExpMP : public GenericMP<ExpMP>
{
BYTE a[1024];
};
int _tmain(int argc, _TCHAR* argv[])
{
ExpMP *aMP = new ExpMP();
delete aMP;
}

相關詞條

熱門詞條

聯絡我們