slab alloc記憶體配置機制

來源:互聯網
上載者:User
slab的“對象重用”

                                      

到目前為止,SUN於1991年發明的Slab Allocator是各種OS核心Memory Allocator中被認為整體效能最好的。它有幾個措施來促進記憶體配置效能的提高,其中之一就是"對象重用"。

原理

OS可以使用Slab提供通用記憶體塊的申請與釋放;所謂通用記憶體塊指的是可以被用於非特定目的的記憶體塊,它們被申請出來之後,可以用做構建一個對象,也可以用做緩衝區。這種情況下,每一個slab cache都是一個提供固定大小的通用記憶體塊的pool。在linux 2.4中,這樣的slab cache有13個,分別用於提供大小為32,64,128,256,......,131072的通用記憶體塊。

除了這樣的slab cache,OS可以用slab提供特定對象的Allocator。在一個提供特定對象的slab cache中,只有一種類型的對象可以分配,不同的特定對象分別由自己特定的slab cache。比如inode,vm_area等等比較複雜而核心又非常頻繁的使用的對象,都擁有自己專用的slab cache。

對於特定類型的slab cache,就可以使用"對象重用"技術。

其實,Slab的"對象重用"對效能的改進,並非純粹意義上的記憶體配置與釋放操作效能上的提高。我們使用C++來從記憶體 Clerk來構建對象時使用如下方式:

type_pointer* __p_object = new type_pointer(__init_para1, _init_para2);

這條語句包含兩個過程:
1)從memory allocator中分配用於構建對象的記憶體;
2)調用class type_pointer的constructor函數,對對象進行構造。

C++的對象銷毀方式如下:

delete __p_object;

這條語句也包含兩個過程:
1)調用class type_pointer的destructor函數;
2)將對象佔用的記憶體釋放回memory allocator中。

一般的Memory Allocator僅僅負責記憶體的釋放與申請,即上述對象構建過程中的過程1和對象銷毀過程的過程2。但slab卻可以在對象構建過程中的過程2和對象銷毀過程的過程1中做了文章。

每一個對象,在傳統的方式下,其生命週期總是包含如下如下五個過程:
1)從memory allocator中分配用於構建對象的記憶體;
2)調用class的constructor函數;
3)使用它;
4)調用class的destructor函數;
5)將對象佔用的記憶體釋放回memory allocator中。

由於Slab在記憶體配置與釋放方面的良好設計,絕大多數情況下,記憶體的分配都非常迅速,在所有情況下,記憶體的釋放操作都非常快捷。相比較之下,對象的constructor和destructor過程佔去了對象的構建與銷毀的大多數時間,對於複雜的對象更是如此。

仔細觀察一下上面列出的五個過程,再稍微讓你的腦細胞做點運動,你就會發現一個重要事實——既然在一個特定對象的slab cache中存放的都是同一對象,如果對象的使用者能夠保證它所擷取的對象在釋放之前將對象恢複到對象剛剛調用constructor函數之後的狀態,那麼這個對象就沒有必要被調用destructor函數;取而代之,我們只需要把這個對象放回slab cache,下一次,都這塊記憶體重新被分配的時候,由於其狀態和調用了constructor之後的狀態是一樣的,所以,也就不再需要重新調用constructor。直到最後,這個slab cache block被返還給更底層的記憶體管理器時,也就是說,這個對象所屬的大塊記憶體不再屬於當前slab cache的時候,再調用其destructor就行了。

所以,對於一個特定對象的slab cache中,所有的對象記憶體塊,在多次的分配與釋放中,只需要被調用constructor一次,也只需要被調用destructor一次,這些很耗時的冗餘過程就清除掉之後,效能自然能夠得到顯著的提高。

在我們為這一重大改進歡欣雀躍的時候,必須牢記這一改進所包含的前提,再說一遍——"調用者必須保證在使用後,把對象恢複到使用前的狀態,即對對象剛剛調用了constructor之後的狀態"。

一個例子

早期,Linux並沒有使用Slab。但Slab的聲譽蒸蒸日上,讓Linux的開發人員心猿意馬,終於按耐不住,換了門庭。

Linux 2.4的slab cache的建立介面為:

kmem_cache_t* kmem_cache_create(const char* __name, size_t __size,
                                size_t __offset, unsigned long __flags,
                                void (*__ctor)(void*, kmem_cache_t*, unsigned long),
                                void (*__dtor)(void*, kmem_cache_t*, unsigned long));

這個函數的最後兩個參數,__ctor和__dtor就是當前slab cache對象的constructor和destructor函數。
下面,我們看一個例子——

假設,我們有一個對象,其類型定義為:
struct object{
  mutex_t lock;
  char*   buffer;
  int     count;
};

其constructor為:
void object_ctor(void* __mem, kmem_cache_t*, unsigned long)
{
   struct object *__obj = (struct object*) __mem;
   mutex_init(&__obj->lock);
   __obj->buffer = NULL;
   __obj->count = 0;
}

其destructor為:
void object_dtor(void* __mem, kmem_cache_t*, unsigned long)
{
   struct object *__obj = (struct object*) __mem;

   assert(__obj->buffer == NULL);
   assert(__obj->count == 0);
   assert(!mutex_test_lock(&__obj->lock));

   mutex_destory(&__obj->lock);  
}

在這個例子中,一個struct object類型的對象在被調用object_ctor後,其狀態為:
1)lock被建立並初始化,處於unlock狀態;
2)buffer指標為NULL;
3)count的值為0。

然後,它就可以使用這個對象,比如下面的代碼:
void foo_change(object* __obj, size_t __count)
{
   mutex_lock(&__obj->lock);
   __obj->buffer = (char*) malloc(__count+1);
   __obj->count = __count;
}

這段代碼改變了__obj的狀態,因為其buffer域和count域都改變了。所以,在將__obj返還給slab之前,使用者必須將它們的狀態還原。

void foo_recovery(object* __obj)
{
   if(__obj->buffer)
      free(__obj->buffer);
   __obj->count = 0;

  if(mutex_test_lock(&__obj->lock))
      mutex_unlock(&__obj->lock);
}

這個例子展示了我們如何使用slab的"對象重用"機制,應該夠了。但我還不想讓自己的大腦這麼快停止對這個問題的思考,我們接著往下看——

上面的例子是用C語言寫的,我們不妨將其改為C++的實現。

class object
{
  mutex_t lock;
  char*   buffer;
  int     count;

public:
  object(void)
  {
     mutex_init(&lock);
     buffer = NULL;
     count = 0;
  }
  ~object()
  {
     assert(buffer == NULL);
     assert(count == 0);
     assert(!mutex_test_lock(&lock));

     mutex_destory(&lock);      
  }
 
  void foo_change(size_t __count)
  {
     mutex_lock(&lock);
     buffer = (char*) malloc(__count+1);
     count = __count;
  }

  void foo_recovery(void)
  {
    if(buffer)
      free(buffer);
    count = 0;

    if(mutex_test_lock(&lock))
      mutex_unlock(&lock);
  }
};

對於C++語言,由於自身存在著ctor/dtor機制,我們使用new從slab allocator中分配一個對象的時候,class的construct會被自動調用;同樣,當我們使用delete將對象銷毀的時候,destructor也會自動被調用,這是語言本身的機制,我們無法迴避它。這樣,每次對象被構造/銷毀的時候,class的constructor函數和destructor函數都要被調用,這種情況下,slab提供的"對象重用"根本沒有用武之地。

難道一種語言就會影響一個這麼重要功能的使用?更何況C++還是一種通用程式設計語言!!我想你已經開始不安,然後憤怒,最後陷入絕望。

在你絕望之前,做幾下深呼吸,然後仔細觀察形勢,認真考慮對策,很可能"柳岸花明"。

在你情緒平穩之後,我們再來看看上面的C語言的例子。

這個例子中,object的對象包含兩部分資源:
1)由object_ctor構造的資源lock;
2)由調用者構造的資源buffer;count也應該算。

上帝是存在的,它在聖經裡教導我們:"塵歸塵,土歸土"。所以我們應該讓slab去做它該做的——構造和銷毀lock;讓class的ctor/dtor構造和銷毀buffer。所以,上面的C++的改寫是錯誤的,正確的實現應該是:

class object
{
public:
  mutex_t lock;
  char*   buffer;
  int     count;

public:
  void object(size_t __count)
  {
     mutex_lock(&lock);
     buffer = (char*) malloc(__count+1);
     count = __count;
     mutex_unlock(&lock);
  }

  void ~object()
  {
    if(buffer)
      free(buffer);
    count = 0;

    if(mutex_test_lock(&lock))
      mutex_unlock(&lock);
  }

public:
  static void ctor(void* __obj,kmem_cache_t*, unsigned long)
  {
     object* __object = static_cast(__obj);
     mutex_init(&__object->lock);
     __object->buffer = NULL;
     object->count = 0;
  }

  static void dtor(void* __obj, kmem_cache_t*, unsigned long)
  {
     object* __object = static_cast(__obj);
     assert(__object->buffer == NULL);
     assert(__object->count == 0);
     assert(!mutex_test_lock(&__object->lock));

     mutex_destory(&__object->lock);
  }
};

函數ctor和dtor就是交給slab去調用的構造和解構函式,非常重要的一點是它們必須是static的,或者乾脆把它們實現為外部過程。

然後,我們就可以這樣來建立其slab cache:

typedef void (*SLAB_CDTOR)(void*, kmem_cache_t*, unsigned long);
kmem_cache_t* object_cache;

object_cache = kmem_cache_create("object cache", __size,
                                __offset, __flags,
                                (SLAB_CDTOR)object::ctor,
                                (SLAB_CDTOR)bject::dtor);

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.