轉載請注明原文地址:http://www.cnblogs.com/ygj0930/p/6539590.html
內核內存管理的一項重要工作就是如何在頻繁申請釋放內存的情況下,避免碎片的產生。這就要求內核采取靈活而恰當的內存分配策略。通常,內存分配一般有兩種情況:大對象(大的連續空間分配)、小對象(小的空間分配)。針對不同的需求,Linux分別采取了伙伴系統算法和SLAB進行內存分配。
伙伴系統:把所有的空閑頁框分為11個塊鏈表,每個塊鏈表中的結點分別是大小為1,2,4,8,16,32,64,128,256,512和1024個連續頁框的頁框塊。最大的頁框塊包含1024個連續頁框,對應4MB大小的連續內存。假設要申請一個256個頁框的塊,則先從結點為256個連續頁框塊的鏈表中查找空閑塊,如果沒有,就去512個頁框的鏈表中找,找到了則將頁框塊分為2個256個頁框的塊,一個分配給應用,另外一個移到256個頁框的鏈表中。如果512個頁框的鏈表中仍沒有空閑塊,繼續向1024個頁框的鏈表查找—分割—分配和轉移。如果仍然沒有,則返回錯誤。使用過的頁框塊在釋放時,會主動將兩個連續的頁框塊合並為一個較大的頁框塊,然后作為結點插入相應規格的鏈表中。
伙伴系統很好地解決了外部碎片(頁框之間的碎片)問題:
如圖,當前內存段中空閑的頁框最大不過連續3個頁框。如果此時申請4個連續頁框大小的內存則只能去更大的空閑內存處截取了,久而久之,這些頁框之間留下的空隙就成為了外部碎片。而伙伴系統對這些外部碎片進行管理(分配、合並),使得內存中的頁框能盡量得到使用。
SLAB:伙伴系統分配內存時是基於頁框為單位的,比較大。如果是幾十個字節的小內存分配怎么辦呢?此時就需要用SLAB機制。slab分配器是基於對象進行管理的,所謂的對象就是內核中的數據結構(例如:task_struct,file_struct 等)。相同類型的對象歸為一類,每當要申請這樣一個對象時,slab分配器就從一個slab列表中分配一個這樣大小的單元出去,而當要釋放時,將其重新保存在該列表中,而不是直接返回給伙伴系統,從而避免內部碎片。slab分配器並不丟棄已經分配的對象,而是釋放並把它們保存在內存中。slab分配對象時,會使用最近釋放的對象的內存塊,因此其駐留在cpu高速緩存中的概率會大大提高。也就是說:在內存中維護一個slab列表,列表項對應這各種數據結構大小的內存塊;當有小對象申請內存時,直接從slab列表中找到對象類型的列表項,把相應大小的內存分配出去;對象用完后,釋放掉對象並把對象所占的內存塊歸還到slab列表以供下一個同類型的對象使用。
同理,由於SLAB是對於小對象的內存分配,很好地解決了頁框內部的內存分配產生的碎片(內部碎片)問題。