記憶體碎片及夥伴演算法
以下是自己今天學習心得:
記憶體碎片概念:
記憶體碎片問題分為內部碎片和外部碎片兩種。
1.內部碎片是由於採用固定大小的記憶體分割槽,當一個程序不能完全使用分給它的固定記憶體區域時,就將該程序分配後剩餘的部分稱為內碎片。通常內碎片難以避免;
2.外部碎片是由於某些未分配的連續區域太小,不足以為任意程序分配記憶體資源的記憶體塊大小,此時稱這些不可用的記憶體塊大小為外部碎片
對於malloc()等函式,每次申請完記憶體後都會釋放,但每次釋放的記憶體大小及釋放時間的不同就會產生記憶體碎片。比如:在記憶體單元100的起始地址到記憶體單元200之間,共申請了100個1位元組的空間。在free()時,釋放了記憶體地址為奇數的記憶體單元(如101,103,105……)而偶數單元不釋放,釋放了50個1位元組空間,雖然總空間數為50位元組,但由於這50個1位元組空間不連續。當下次要申請2位元組的記憶體單元時,卻無法在100到200的記憶體地址單元中申請到空間,於是就產生記憶體碎片問題。
為什麼會產生記憶體碎片?
對於記憶體的分配方法有:連續地址分配、分頁機制和分段機制以及段頁式(網上有很多關於記憶體地址分配的文章,都很不錯,可以瞭解一下)
連續地址分配:固定分割槽分配會產生內碎片問題,動態分割槽分配會產生外碎片問題
分頁機制:相比較固定分配分割槽,內碎片問題已經明顯減少
分段機制:消除內碎片問題,但會產生外碎片問題
夥伴演算法可以解決外碎片問題,其演算法思想如下:
無論已經分配的分割槽還是空閒分割槽,其大小均為2的k次冪,k為整數,1<=k<=m,其中2^1表示分配的最小分割槽大小,2^m 表示分配的最大分割槽的大小。在系統開始執行時,整個記憶體區是一個大小為2^m的空閒分割槽,隨著系統執行,空閒區的不斷劃分會形成若干不連續的空閒分割槽,將這些空閒分割槽按照分割槽的大小進行分類,對於每一類具有相同大小的所有空閒分割槽,單獨成立一個空閒分割槽雙向連結串列。這樣,不同大小的空閒分割槽就形成了k(0<=k<=m)個空閒分割槽連結串列。
當需要為程序分配一個長度為n 的儲存空間時,首先計算i 的值,使2^(i-1)< n < 2^ i ,然後在空閒分割槽大小為i 的空閒區連結串列中查詢。若找到,則把該空閒分割槽分配給該程序。否則,表明長度為2^ i 的空閒分割槽已經耗盡,則在長度為2^(i+1)的空閒分割槽連結串列中查詢。若存在大小為2^(i+1)的空閒分割槽,則將該分割槽分為連個大小均為2^ i 的塊,一個塊分配給該程序,一個塊掛載在長度大小為2^ i 的空閒分割槽連結串列中。若大小為2^(i+1)的空閒分割槽也已經耗盡,則尋找大小為2^(i+2)的空閒分割槽,若找到,則對該分割槽進行兩次劃分,第一次劃分,將大小為2^(i+2)的分割槽分為兩個大小為2^(i+1)的空閒分割槽,一個掛載在大小為2^(i+1)的空閒分割槽上,一個再次分割為兩個大小為2^ i 的分割槽,一個掛載在大小為2^ i 的空閒分割槽上,一個用於分配給該程序;如果未找到大小為2^ ( i+2 ) 的空閒分割槽,則在2^( i+3) 的空閒分割槽上尋找,一次重複以上步驟,指導找到空閒區
在Linux中將這記憶體分為10個空閒分割槽連結串列,0-9,大小範圍是:2^0 - 2^9
以上過程的逆過程就是夥伴演算法的釋放過程,釋放過程需要滿足兩個條件:1.兩個塊具有相同的大小 2.它們的實體地址是連續的
也正是基於以上兩個條件才稱該演算法為夥伴演算法
Linux夥伴演算法中涉及到的資料結構:
free_area_t free_area[MAX_ORDER];
我們再次對free_area_t 給予較詳細的描述。
#difine MAX_ORDER 10
type struct free_area_struct {
struct list_head free_list
unsigned int *map
} free_area_t
其中list_head域是一個通用的雙向連結串列結構,連結串列中元素的型別將為mem_map_t(即struct page結構)。Map域指向一個位圖,其大小取決於現有的頁面數。free_area第k項點陣圖的每一位,描述的就是大小為2k個頁面的兩個夥伴塊的狀態。如果點陣圖的某位為0,表示一對兄弟塊中或者兩個都空閒,或者兩個都被分配,如果為1,肯定有一塊已被分配。當兄弟塊都空閒時,核心把它們當作一個大小為2k+1的單獨快來處理