亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 1162 | 回復: 0
打印 上一主題 下一主題

Linux slab 分配器詳解 [復制鏈接]

論壇徽章:
0
跳轉到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2009-06-15 18:15 |只看該作者 |倒序瀏覽

2007-07-02 16:25
良好的操作系統(tǒng)性能部分依賴于操作系統(tǒng)有效管理資源的能力。在過去,堆內存管理器是實際的規(guī)范,但是其性能會受到內存碎片和內存 回收需求的影響,F(xiàn)在,Linux® 內核使用了源自于 Solaris的一種方法,但是這種方法在嵌入式系統(tǒng)中已經使用了很長時間了,它是將內存作為對象按照大小進行分配。本文將探索 slab分配器背后所采用的思想,并介紹這種方法提供的接口和用法。         動態(tài)內存管理
         內存管理的目標是提供一種方法,為實現(xiàn)各種目的而在各個用戶之間實現(xiàn)內存共享。內存管理方法應該實現(xiàn)以下兩個功能:
         

  • 最小化管理內存所需的時間
  • 最大化用于一般應用的可用內存(最小化管理開銷)
         內存管理實際上是一種關于權衡的零和游戲。您可以開發(fā)一種使用少量內存進行管理的算法,但是要花費更多時間來管理可用內存。也可以開發(fā)一個算法來有效地管理內存,但卻要使用更多的內存。最終,特定應用程序的需求將促使對這種權衡作出選擇。
         每個內存管理器都使用了一種基于堆的分配策略。在這種方法中,大塊內存(稱為 )用來為用戶定義的目的提供內存。當用戶需要一塊內存時,就請求給自己分配一定大小的內存。堆管理器會查看可用內存的情況(使用特定算法)并返回一塊內存。搜索過程中使用的一些算法有 first-fit(在堆中搜索到的第一個滿足請求的內存塊)和 best-fit(使用堆中滿足請求的最合適的內存塊)。當用戶使用完內存后,就將內存返回給堆。
         這種基于堆的分配策略的根本問題是碎片(fragmentation)。當內存塊被分配后,它們會以不同的順序在不同的時間返回。這樣會在堆中留下一些洞,需要花一些時間才能有效地管理空閑內存。這種算法通常具有較高的內存使用效率(分配需要的內存),但是卻需要花費更多時間來對堆進行管理。
         另外一種方法稱為 buddy memory allocation, 是一種更快的內存分配技術,它將內存劃分為 2 的冪次方個分區(qū),并使用 best-fit 方法來分配內存請求。當用戶釋放內存時,就會檢查buddy 塊,查看其相鄰的內存塊是否也已經被釋放。如果是的話,將合并內存塊以最小化內存碎片。這個算法的時間效率更高,但是由于使用best-fit 方法的緣故,會產生內存浪費。
         本文將著重介紹 Linux 內核的內存管理,尤其是 slab 分配提供的機制。
         slab 緩存
         Linux所使用的 slab 分配器的基礎是 Jeff Bonwick 為 SunOS 操作系統(tǒng)首次引入的一種算法。Jeff的分配器是圍繞對象緩存進行的。在內核中,會為有限的對象集(例如文件描述符和其他常見結構)分配大量內存。 Jeff發(fā)現(xiàn)對內核中普通對象進行初始化所需的時間超過了對其進行分配和釋放所需的時間。因此他的結論是不應該將內存釋放回一個全局的內存池,而是將內存 保持為針對特定目而初始化的狀態(tài)。例如,如果內存被分配給了一個互斥鎖,那么只需在為互斥鎖首次分配內存時執(zhí)行一次互斥鎖初始化函數(shù) (mutex_init)即可。后續(xù)的內存分配不需要執(zhí)行這個初始化函數(shù),因為從上次釋放和調用析構之后,它已經處于所需的狀態(tài)中了。
         Linux slab 分配器使用了這種思想和其他一些思想來構建一個在空間和時間上都具有高效性的內存分配器。
         圖 1 給出了 slab 結構的高層組織結構。在最高層是 cache_chain,這是一個 slab 緩存的鏈接列表。這對于 best-fit 算法非常有用,可以用來查找最適合所需要的分配大小的緩存(遍歷列表)。cache_chain 的每個元素都是一個        kmem_cache 結構的引用(稱為一個 cache)。它定義了一個要管理的給定大小的對象池。
                        
圖   1. slab 分配器的主要結構
            

         
         每個緩存都包含了一個 slabs 列表,這是一段連續(xù)的內存塊(通常都是頁面)。存在 3 種 slab:
                            slabs_full             完全分配的 slab                    slabs_partial             部分分配的 slab                    slabs_empty             空 slab,或者沒有對象被分配          注意 slabs_empty 列表中的 slab 是進行回收(reaping)的主要備選對象。正是通過此過程,slab 所使用的內存被返回給操作系統(tǒng)供其他用戶使用。
         slab列表中的每個 slab 都是一個連續(xù)的內存塊(一個或多個連續(xù)頁),它們被劃分成一個個對象。這些對象是從特定緩存中進行分配和釋放的基本元素。注意slab 是 slab 分配器進行操作的最小分配單位,因此如果需要對 slab 進行擴展,這也就是所擴展的最小值。通常來說,每個 slab被分配為多個對象。
         由于對象是從 slab 中進行分配和釋放的,因此單個 slab 可以在 slab 列表之間進行移動。例如,當一個 slab 中的所有對象都被使用完時,就從 slabs_partial 列表中移動到       slabs_full 列表中。當一個 slab 完全被分配并且有對象被釋放后,就從 slabs_full 列表中移動到       slabs_partial 列表中。當所有對象都被釋放之后,就從 slabs_partial 列表移動到       slabs_empty 列表中。
         slab 背后的動機
         與傳統(tǒng)的內存管理模式相比, slab緩存分配器提供了很多優(yōu)點。首先,內核通常依賴于對小對象的分配,它們會在系統(tǒng)生命周期內進行無數(shù)次分配。slab緩存分配器通過對類似大小的對 象進行緩存而提供這種功能,從而避免了常見的碎片問題。slab分配器還支持通用對象的初始化,從而避免了為同一目而對一個對象重復進行初始化。最后, slab分配器還可以支持硬件緩存對齊和著色,這允許不同緩存中的對象占用相同的緩存行,從而提高緩存的利用率并獲得更好的性能。
         
API 函數(shù)
         現(xiàn)在來看一下能夠創(chuàng)建新 slab 緩存、向緩存中增加內存、銷毀緩存的應用程序接口(API)以及 slab 中對對象進行分配和釋放操作的函數(shù)。
         第一個步驟是創(chuàng)建 slab 緩存結構,您可以將其靜態(tài)創(chuàng)建為:
         
struct struct kmem_cache *my_cachep;
   然后其他 slab 緩存函數(shù)將使用該引用進行創(chuàng)建、刪除、分配等操作。kmem_cache 結構包含了每個中央處理器單元(CPU)的數(shù)據(jù)、一組可調整的(可以通過 proc 文件系統(tǒng)訪問)參數(shù)、統(tǒng)計信息和管理 slab 緩存所必須的元素。
         kmem_cache_create
         內核函數(shù) kmem_cache_create 用來創(chuàng)建一個新緩存。這通常是在內核初始化時執(zhí)行的,或者在首次加載內核模塊時執(zhí)行。其原型定義如下:
         
struct kmem_cache *
kmem_cache_create( const char *name, size_t size, size_t align,
                      unsigned long flags;
                      void (*ctor)(void*, struct kmem_cache *, unsigned long),
                      void (*dtor)(void*, struct kmem_cache *, unsigned long));
                        name 參數(shù)定義了緩存名稱,proc 文件系統(tǒng)(在 /proc/slabinfo 中)使用它標識這個緩存。        size 參數(shù)指定了為這個緩存創(chuàng)建的對象的大小, align 參數(shù)定義了每個對象必需的對齊。        flags 參數(shù)指定了為緩存啟用的選項。這些標志如表 1 所示。
         
表 1.       kmem_cache_create 的部分選項(在 flags 參數(shù)中指定)
         [tr]選項說明[/tr][tr]SLAB_RED_ZONE[td]在對象頭、尾插入標志,用來支持對緩沖區(qū)溢出的檢查。[tr] SLAB_POISON[td]使用一種己知模式填充 slab,允許對緩存中的對象進行監(jiān)視(對象屬對象所有,不過可以在外部進行修改)。[tr]SLAB_HWCACHE_ALIGN[td]指定緩存對象 必須與硬件緩存行對齊。
         
                        ctor 和 dtor 參數(shù)定義了一個可選的對象構造器和析構器。構造器和析構器是用戶提供的回調函數(shù)。當從緩存中分配新對象時,可以通過構造器進行初始化。
         在創(chuàng)建緩存之后,       kmem_cache_create 函數(shù)會返回對它的引用。注意這個函數(shù)并沒有向緩存分配任何內存。相反,在試圖從緩存(最初為空)分配對象時,refill 操作將內存分配給它。當所有對象都被使用掉時,也可以通過相同的操作向緩存添加內存。
         kmem_cache_destroy
         內核函數(shù) kmem_cache_destroy 用來銷毀緩存。這個調用是由內核模塊在被卸載時執(zhí)行的。在調用這個函數(shù)時,緩存必須為空。
         
void kmem_cache_destroy( struct kmem_cache *cachep );
         kmem_cache_alloc
         要從一個命名的緩存中分配一個對象,可以使用        kmem_cache_alloc 函數(shù)。調用者提供了從中分配對象的緩存以及一組標志:
         
void kmem_cache_alloc( struct kmem_cache *cachep, gfp_t flags );
         這個函數(shù)從緩存中返回一個對象。注意如果緩存目前為空,那么這個函數(shù)就會調用        cache_alloc_refill 向緩存中增加內存。 kmem_cache_alloc 的 flags 選項與       kmalloc 的 flags 選項相同。表 2 給出了標志選項的部分列表。
         
表 2.       kmem_cache_alloc 和 kmalloc 內核函數(shù)的標志選項
         [tr]標志說明[/tr][tr]GFP_USER[td]為用戶分配內存(這個調用可能會睡眠)。[tr]GFP_KERNEL[td]從內核 RAM 中分配內存(這個調用可能會睡眠)。[tr]GFP_ATOMIC[td]使該調用強制處于非睡眠狀態(tài)(對中斷處理程序非常有用)。[tr] GFP_HIGHUSER[td]從高端內存中分配內存。
         
         kmem_cache_zalloc
         內核函數(shù) kmem_cache_zalloc 與        kmem_cache_alloc 類似,只不過它對對象執(zhí)行       memset 操作,用來在將對象返回調用者之前對其進行清除操作。
         kmem_cache_free
         要將一個對象釋放回 slab,可以使用       kmem_cache_free。調用者提供了緩存引用和要釋放的對象。
         
void kmem_cache_free( struct kmem_cache *cachep, void *objp );
         kmalloc 和 kfree
         內核中最常用的內存管理函數(shù)是       kmalloc 和 kfree 函數(shù)。這兩個函數(shù)的原型如下:
         
void *kmalloc( size_t size, int flags );
void kfree( const void *objp );
         注意在 kmalloc 中,惟一兩個參數(shù)是要分配的對象的大小和一組標志(請參看
表 2
中的部分列表)。但是 kmalloc 和        kfree 使用了類似于前面定義的函數(shù)的 slab 緩存。kmalloc 沒有為要從中分配對象的某個 slab 緩存命名,而是循環(huán)遍歷可用緩存來查找可以滿足大小限制的緩存。找到之后,就(使用       __kmem_cache_alloc)分配一個對象。要使用       kfree 釋放對象,從中分配對象的緩存可以通過調用 virt_to_cache 確定。這個函數(shù)會返回一個緩存引用,然后在       __cache_free 調用中使用該引用釋放對象。
         
         其他函數(shù)
         slab 緩存 API 還提供了其他一些非常有用的函數(shù)。        kmem_cache_size 函數(shù)會返回這個緩存所管理的對象的大小。您也可以通過調用 kmem_cache_name 來檢索給定緩存的名稱(在創(chuàng)建緩存時定義)。緩存可以通過釋放其中的空閑 slab 進行收縮。這可以通過調用       kmem_cache_shrink 實現(xiàn)。注意這個操作(稱為回收)是由內核定期自動執(zhí)行的(通過        kswapd)。
         
unsigned int kmem_cache_size( struct kmem_cache *cachep );
const char *kmem_cache_name( struct kmem_cache *cachep );
int kmem_cache_shrink( struct kmem_cache *cachep );
         
slab 緩存的示例用法
         下面的代碼片斷展示了創(chuàng)建新 slab 緩存、從緩存中分配和釋放對象然后銷毀緩存的過程。首先,必須要定義一個 kmem_cache 對象,然后對其進行初始化(請參看清單 1)。這個特定的緩存包含 32 字節(jié)的對象,并且是硬件緩存對齊的(由標志參數(shù) SLAB_HWCACHE_ALIGN 定義)。
         
清單 1. 創(chuàng)建新 slab 緩存
            
static struct kmem_cache *my_cachep;
static void init_my_cache( void )
{
my_cachep = kmem_cache_create(
               "my_cache",          /* Name */
               32,                    /* Object Size */
               0,                   /* Alignment */
               SLAB_HWCACHE_ALIGN, /* Flags */
               NULL, NULL );       /* Constructor/Deconstructor */
return;
}
         使用所分配的 slab   緩存,您現(xiàn)在可以從中分配一個對象了。清單 2 給出了一個從緩存中分配和釋放對象的例子。它還展示了兩個其他函數(shù)的用法。
         
清單 2. 分配和釋放對象
            
int slab_test( void )
{
   void *object;
   printk( "Cache name is %s\n", kmem_cache_name( my_cachep ) );
   printk( "Cache object size is %d\n", kmem_cache_size( my_cachep ) );
   object = kmem_cache_alloc( my_cachep, GFP_KERNEL );
   if (object) {
kmem_cache_free( my_cachep, object );
   }
   return 0;
}
         最后,清單 3 演示了 slab 緩存的銷毀。調用者必須確保在執(zhí)行銷毀操作過程中,不要從緩存中分配對象。
         
清單 3. 銷毀 slab 緩存
            
static void remove_my_cache( void )
{
   if (my_cachep) kmem_cache_destroy( my_cachep );
   return;
}
         
slab 的 proc 接口
         proc文件系統(tǒng)提供了一種簡單的方法來監(jiān)視系統(tǒng)中所有活動的 slab 緩存。這個文件稱為/proc/slabinfo,它除了提供一些可以從用戶空間訪問的可調整參數(shù)之外,還提供了有關所有 slab 緩存的詳細信息。當前版本的slabinfo 提供了一個標題,這樣輸出結果就更具可讀性。對于系統(tǒng)中的每個 slab緩存來說,這個文件提供了對象數(shù)量、活動對象數(shù)量以及對象大小的信息(除了每個 slab 的對象和頁面之外)。另外還提供了一組可調整的參數(shù)和slab 數(shù)據(jù)。
         要調優(yōu)特定的 slab 緩存,可以簡單地向   /proc/slabinfo 文件中以字符串的形式回轉 slab 緩存名稱和 3 個可調整的參數(shù)。下面的例子展示了如何增加 limit 和 batchcount 的值,而保留 shared        factor 不變(格式為 “cache name limit batchcount shared factor”):
         
# echo "my_cache 128 64 8" > /proc/slabinfo
         
                        limit 字段表示每個 CPU 可以緩存的對象的最大數(shù)量。        batchcount 字段是當緩存為空時轉換到每個 CPU 緩存中全局緩存對象的最大數(shù)量。        shared 參數(shù)說明了對稱多處理器(Symmetric MultiProcessing,SMP)系統(tǒng)的共享行為。
         注意您必須具有超級用戶的特權才能在 proc 文件系統(tǒng)中為 slab 緩存調優(yōu)參數(shù)。
         
SLOB 分配器
         對于小型的嵌入式系統(tǒng)來說,存在一個 slab 模擬層,名為 SLOB。這個 slab 的替代品在小型嵌入式 Linux 系統(tǒng)中具有優(yōu)勢,但是即使它保存了 512KB 內存,依然存在碎片和難于擴展的問題。在禁用 CONFIG_SLAB 時,內核會回到這個 SLOB 分配器中。更多信息請參看
參考資料
一節(jié)。
         
結束語
       slab 緩存分配器的源代碼實際上是 Linux 內核中可讀性較好的一部分。除了函數(shù)調用的間接性之外,源代碼也非常直觀,總的來說,具有很好的注釋。如果您希望了解更多有關 slab 緩存分配器的內容,建議您從源代碼開始,因為它是有關這種機制的最新文檔。        下面的
參考資料
一節(jié)提供了介紹 slab 緩存分配器的參考資料,但是不幸的是就目前的 2.6 實現(xiàn)來說,這些文檔都已經過時了。
      


本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u3/94189/showart_1965121.html
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復

  

北京盛拓優(yōu)訊信息技術有限公司. 版權所有 京ICP備16024965號-6 北京市公安局海淀分局網監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關心和支持過ChinaUnix的朋友們 轉載本站內容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP