内存管理算法--Buddy伙伴算法
http://blog.csdn.net/orange_os/article/details/7392986
采用buddy算法,解決了外碎片問題,這種方法適合大塊內存請求,這是因為把頁作為單位內存塊,不適合小內存區請求。如:幾十個或者幾百個字節。
對于小內存的請求,slab算法可能更好的解決內碎片問題。
Buddy算法的優缺點:
1)盡管伙伴內存算法在內存碎片問題上已經做的相當出色,但是該算法中,一個很小的塊往往會阻礙一個大塊的合并,一個系統中,對內存塊的分配,大小是隨機的,一片內存中僅一個小的內存塊沒有釋放,旁邊兩個大的就不能合并。
2)算法中有一定的浪費現象,伙伴算法是按2的冪次方大小進行分配內存塊,當然這樣做是有原因的,即為了避免把大的內存塊拆的太碎,更重要的是使分配和釋放過程迅速。但是他也帶來了不利的一面,如果所需內存大小不是2的冪次方,就會有部分頁面浪費。有時還很嚴重。比如原來是1024個塊,申請了16個塊,再申請600個塊就申請不到了,因為已經被分割了。
3)另外拆分和合并涉及到 較多的鏈表和位圖操作,開銷還是比較大的。
Buddy(伙伴的定義):
這里給出伙伴的概念,滿足以下三個條件的稱為伙伴:
1)兩個塊大小相同;
2)兩個塊地址連續;
3)兩個塊必須是同一個大塊中分離出來的;
Buddy算法的分配原理:
假如系統需要4(2*2)個頁面大小的內存塊,該算法就到free_area[2]中查找,如果鏈表中有空閑塊,就直接從中摘下并分配出去。如果沒有,算法將順著數組向上查找free_area[3],如果free_area[3]中有空閑塊,則將其從鏈表中摘下,分成等大小的兩部分,前四個頁面作為一個塊插入free_area[2],后4個頁面分配出去,free_area[3]中也沒有,就再向上查找,如果free_area[4]中有,就將這16(2*2*2*2)個頁面等分成兩份,前一半掛如free_area[3]的鏈表頭部,后一半的8個頁等分成兩等分,前一半掛free_area[2]
的鏈表中,后一半分配出去。假如free_area[4]也沒有,則重復上面的過程,知道到達free_area數組的最后,如果還沒有則放棄分配。
Buddy算法的釋放原理:
內存的釋放是分配的逆過程,也可以看作是伙伴的合并過程。當釋放一個塊時,先在其對應的鏈表中考查是否有伙伴存在,如果沒有伙伴塊,就直接把要釋放的塊掛入鏈表頭;如果有,則從鏈表中摘下伙伴,合并成一個大塊,然后繼續考察合并后的塊在更大一級鏈表中是否有伙伴存在,直到不能合并或者已經合并到了最大的塊(2*2*2*2*2*2*2*2*2個頁面)。
整個過程中,位圖扮演了重要的角色,如圖2所示,位圖的某一位對應兩個互為伙伴的塊,為1表示其中一塊已經分配出去了,為0表示兩塊都空閑?;锇橹袩o論是分配還是釋放都只是相對的位圖進行異或操作。分配內存時對位圖的
是為釋放過程服務,釋放過程根據位圖判斷伙伴是否存在,如果對相應位的異或操作得1,則沒有伙伴可以合并,如果異或操作得0,就進行合并,并且繼續按這種方式合并伙伴,直到不能合并為止。
Buddy內存管理的實現:
提到buddy 就會想起linux 下的物理內存的管理 ,這里的memory pool 上實現的 buddy 系統
和linux 上按page 實現的buddy系統有所不同的是,他是按照字節的2的n次方來做block的size
實現的機制中主要的結構如下:
整個buddy 系統的結構:
struct mem_pool_table
{
#define MEM_POOL_TABLE_INIT_COOKIE?(0x62756479)
uint32 initialized_cookie;?/*?Cookie 指示內存已經被初始化后的魔數,? 如果已經初始化設置為0x62756479*/
uint8?*mem_pool_ptr;/*?指向內存池的地址*/
uint32 mem_pool_size;?/*?整個pool 的size,下面是整個max block size 的大小*/
boolean assert_on_empty;?/*?如果該值被設置成TRUE,內存分配請求沒有完成就返回 并輸出出錯信息*/
?uint32 mem_remaining;?/*?當前內存池中剩余內存字節數*/??????????????????????????????????????????????
uint32 max_free_list_index;?/*?最大freelist 的下標,*/
struct mem_free_hdr_type ????*free_lists[MAX_LEVELS];/*?這個就是伙伴系統的level數組*/
#ifdef FEATURE_MEM_CHECK
uint32 max_block_requested;
? uint32 min_free_mem;?/*?放mem_remaining?*/
#endif?/*?FEATURE_ONCRPC_MEM_CHECK*/
};
這個結構是包含在free node 或alloc node 中的結構:
其中check 和 fill 都被設置為某個pattern
用來檢查該node 的合法性
#define MEM_HDR_CHECK_PATTERN ((uint16)0x3CA4)
#define MEM_HDR_FILL_PATTERN ((uint8)0x5C)
typedef struct? tagBuddyMemBlockHeadType
{
??? mem_pool_type pool;?/*回指向內存池*/
??? uint16 check;?
??? uint8 state;?/*?bits 0-3 放該node 屬于那1級 bit 7 如果置1,表示已經分配(not?free)
??? uint8 fill;
}?BUDDY_MEM_BLOCK_HEAD_TYPE;
這個結構就是包含node 類型結構的 free header 的結構:
typedef struct? tagBuddyMemHeadType
{
??? mem_node_hdr_type hdr;
??? struct mem_free_hdr_type * pNext;?? /* next,prev,用于連接free header的雙向 list*/
??? struct mem_free_hdr_type * pPrev;
} mem_free_hdr_type;
這個結構就是包含node 類型結構的 alloc header 的結構:已分配的mem 的node 在內存中就是這樣表示的
比如申請size=2000bytes, 按size to level 應該是2048,實際in_use_size
為2000,剩下48byte 全部填充為某一數值,然后在以后free 是可以check?
是否有overwite 到著48byte 中的數值,一般為了速度,只 檢查8到16byte
另外為什么不把這剩下的48byte 放到freelist 中其他level 中呢,這個可能
因為本來buddy 系統的缺點就是容易產生碎片,這樣的話就更碎了
關于free or alloc node 的示意圖:
假設
最小塊為2^4=16,著是由mem_alloc_hdr_type (12byte)決定的, 實際可分配4byte
如果假定最大max_block_size =1024,
如果pool 有mem_free_hdr_type[0]上掛了兩個1024的block node
上圖是free node, 下圖紫色為alloc node
接下來主要是buddy 系統的操作主要包括pool init , mem alloc ,mem free
pool init :
?1. 將實際pool 的大小去掉mem_pool_table 結構大小后的size 放到
???? mem_pool_size, 并且修改實際mem_pool_ptr指向前進mem_pool_table
???? 結構大小的地址
?2.? 接下來主要將mem_pool_size 大小的內存,按最大塊掛到free_lists 上
??? level 為0的list 上,然后小于該level block size 部分,繼續掛大下一
??? 級,循環到全部處理完成? (感覺實際用于pool的size ,應該為減去
??? mem_pool_table 的大小,然后和最大塊的size 對齊,這樣比較好,
??? 但沒有實際測試過)
????
????
mem alloc:
??? 這部分相當簡單,先根據請求mem的size ,實際分配時需要加上mem_alloc_hdr_type
這12byte ,然后根據調整后的size,計算實際應該在那個 level上分配,如果有相應級
很簡單,直接返回,如果沒有,一級一級循環查找,找到后,把省下的部分,在往下一級
一級插入到對應級的freelist 上
mem free:
???? 其中free 的地址,減去12 就可以獲得mem_alloc_hdr_type 結構
???? 然后確定buddy 在該被free block 前,還是后面, 然后合并buddy,
???? 循環尋找上一級的buddy ,有就再合并,只到最大block size 那級
關于這個算法,在<<The Art? of Computer Programming>> vol 1,的
動態存儲分配中有描述,對于那些只有OSAL 的小系統,該算法相當有用
網上有很簡單的buddy算法的實現,但是只是toy,總內存大小不能動態調整
http://coolshell.cn/articles/10427.html
buddy2_new只是申請了一塊小內存,用于管理大內存的分配,設這塊大內存的起始地址是realstart.對于buddy2而言,是按照page管理大內存的。
對于buddy2_alloc返回的偏移是offset, 而實際的內存偏移應該是 realstart+offset*pageSize
總結
以上是生活随笔為你收集整理的内存管理算法--Buddy伙伴算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java split
- 下一篇: 图模型(graphical model,