記憶體配置演算法之夥伴演算法

來源:互聯網
上載者:User
References:
  1. http://blog.csdn.net/zhouzhanglong/archive/2009/04/17/4086349.aspx
Next Fit演算法

首先為了作一個對比,使用了一個來自於《C程式設計語言》上面的malloc版本來進行比對。

這個簡單版本的思想是使用一個迴圈單鏈表來進行空閑塊的維護,base變數表示該鏈表的頭,freep表示上次操作的(malloc或者free)的空閑塊。

分配一定大小的塊的時候,從freep開始找,知道找到一個空閑塊其大小大於等於請求塊的大小。如果大小剛好,則直接分配出去,否則從尾部開始分配請求大小的塊,剩餘的大小就變成新的空閑塊,這樣做的好處是不用維護一個指向freep前一個空閑塊的指標,而只需要改變size值就可以。這個演算法在作業系統的術語裡面叫做Next Fit。

這個演算法具有一定的隨機性的思想,所以總體來說塊的分配會比較平均。

 

以下是malloc.h的內容:

#ifndef _MALLOC_H_<br />#define _MALLOC_H_<br />typedef long Align;<br />union header {<br /> struct {<br /> union header *ptr;<br /> unsigned size;<br /> } s;<br /> Align x; /* used for alignment */<br />};<br />typedef union header Header;<br />void *malloc(unsigned);<br />void free(void *);<br />#endif

 

下面是malloc和free的函數定義:

#include "malloc.h"<br />#include <stddef.h><br />static Header base; /* the beginning of the free block list */<br />static Header *freep = NULL; /* pointer to the block we just dealing with */<br />static Header *morecore(unsigned nu);<br />void *malloc(unsigned nbytes)<br />{<br /> Header *p, *prevp;<br /> unsigned nunits;<br /> nunits = (nbytes + sizeof(Header) - 1) / sizeof(Header) + 1;<br /> if ((prevp = freep) == NULL)<br /> {<br /> base.s.ptr = freep = prevp = &base;<br /> base.s.size = 0;<br /> }<br /> for (p = prevp->s.ptr; ; prevp = p, p = p->s.ptr)<br /> {<br /> if (p->s.size >= nunits)<br />{<br /> if (p->s.size == nunits)<br /> prevp->s.ptr = p->s.ptr;<br /> else<br /> {<br /> p->s.size -= nunits;<br /> p += p->s.size;<br /> p->s.size = nunits;<br /> }<br /> freep = prevp;<br /> return (void *)(p + 1);<br />}<br /> if (p == freep)<br />if ((p = morecore(nunits)) == NULL)<br /> return NULL;<br /> }<br /> return 0;<br />}<br />void free(void *ap)<br />{<br /> Header *bp, *p;<br /> bp = (Header *)ap - 1;<br /> for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)<br /> if (p >= p->s.ptr && (bp > p || bp < p->s.ptr))<br /> break;<br /> if (bp + bp->s.size == p->s.ptr)<br /> {<br /> bp->s.size += p->s.ptr->s.size;<br /> bp->s.ptr = p->s.ptr->s.ptr;<br /> }<br /> else<br /> {<br /> bp->s.ptr = p->s.ptr;<br /> }<br /> if (p + p->s.size == bp)<br /> {<br /> p->s.size += bp->s.size;<br /> p->s.ptr = bp->s.ptr;<br /> }<br /> else<br /> p->s.ptr = bp;<br /> freep = p;<br />}<br />#define NALLOC 1024<br />static Header *morecore(unsigned nu)<br />{<br /> char *cp, *sbrk(int);<br /> Header *up;<br /> if (nu < NALLOC)<br /> nu = NALLOC;<br /> cp = sbrk(nu * sizeof(Header));<br /> if (cp == (char *) -1)<br /> return NULL;<br /> up = (Header *) cp;<br /> up->s.size = nu;<br /> free((void *)(up + 1));<br /> return freep;<br />}

Buddy Algorithm(夥伴演算法)

就如Reference裡面的演算法描述,夥伴演算法的核心思想是把塊的大小都定好,一般是2的冪的大小。

在這裡我們會有一個最大的冪值,稱為u,也就是最大塊就是2^u那麼大。

有一個最小冪值,稱為l,最小塊的大小就是2^l那麼大。

然後不同大小的塊使用不同的鏈表來管理,總的是一個free_area的數組來儲存這些鏈表的表頭。其中數組free_area裡面的下標表示的就是這個鏈表對應的塊的大小的冪數。

然後在分配空閑塊的時候首先從最小的大於申請大小的塊開始尋找。如果這個申請塊的冪數是7那麼就從free_area[7]開始尋找。如果這個鏈表為空白,則往大的鏈表開始搜尋,如果搜尋到了的話,將大塊分割成兩個小塊,其中一塊用來分配,另外一塊用來存在小的塊的鏈表裡面。

 

這裡我的實現是使用了雙向鏈表來實現。

而在free的過程中,就查看這個釋放塊的相鄰塊是否也是空閑,這裡相鄰塊稱為夥伴(buddy)。如果夥伴也是在空閑塊的塊的話,那麼就將這兩個塊合并,這個過程遞迴,不能再合并為止。

下面是buddy.h裡面關於函數的定義:

#ifndef _BUDDY_H_<br />#define _BUDDY_H_<br />void *buddy_alloc(unsigned int);<br />void buddy_free(void *);<br />void buddy_showblocks();<br />#endif

下面相關的資料結構和函數定義:

#include "buddy.h"<br />#include <stdio.h><br />#include <stddef.h><br />/* u = 15, l = 6 */<br />#define BUF_SIZE 32768<br />#define MIN_ORDER 6<br />#define MAX_ORDER 15<br />typedef long Align;<br />union header {<br /> struct {<br /> union header *next;<br /> union header *prev;<br /> unsigned size;<br /> } s;<br /> Align x; /* used for alignment */<br />};<br />typedef union header Header;<br />static char buffer[BUF_SIZE];<br />static Header *free_area[MAX_ORDER + 1] = {NULL};<br />static Header *free_buf = NULL;<br />unsigned int round2power(unsigned int num)<br />{<br /> unsigned int init = 1 << MIN_ORDER;<br /> unsigned int count = MIN_ORDER;<br /> while (init < num)<br /> {<br /> init <<= 1;<br /> count++;<br /> }<br /> return count <= MAX_ORDER? count : MAX_ORDER;<br />}<br />static void insert(Header **listp, Header *block)<br />{<br /> Header *prev = NULL, *current = *listp;</p><p> if (*listp == NULL)<br /> {<br /> *listp = block;<br /> block->s.prev = NULL;<br /> block->s.next = NULL;<br /> return;<br /> }<br /> while (current != NULL)<br /> {<br /> if (prev == NULL && block < current)<br />{<br /> *listp = block;<br /> block->s.next = current;<br /> current->s.prev = block;<br /> block->s.prev = NULL;<br /> return;<br />}<br /> if(block < current)<br />{<br /> prev->s.next = block;<br /> block->s.prev = prev;<br /> block->s.next = current;<br /> current->s.prev = block;<br /> return;<br />}</p><p> prev = current;<br /> current = current->s.next;<br /> }<br /> if (prev != NULL)<br /> {<br /> prev->s.next = block;<br /> block->s.prev = prev;<br /> block->s.next = NULL;<br /> return;<br /> }<br />}<br />static void delete(Header **listp, Header *block)<br />{<br /> if (*listp == NULL)<br /> return;<br /> /* If the block is the head of the list */<br /> if (*listp == block)<br /> {<br /> *listp = block->s.next;<br /> }<br /> if (block->s.prev != NULL)<br /> {<br /> block->s.prev->s.next = block->s.next;<br /> }<br /> if (block->s.next != NULL)<br /> {<br /> block->s.next->s.prev = block->s.prev;<br /> }<br /> return;<br />}<br />static int continous(Header *p1, Header *p2)<br />{<br /> char *ptr1 = (char *) p1;<br /> char *ptr2 = (char *) p2;<br /> return (ptr1 + p1->s.size) == ptr2;<br />}<br />static Header *combine(Header *p1, Header *p2)<br />{<br /> if (p1->s.size != p2->s.size)<br /> return NULL;<br /> p1->s.size <<= 1;<br /> return p1;<br />}<br />void *buddy_alloc(unsigned int nbytes)<br />{<br /> Header *p;<br /> unsigned nunits, idx;<br /> int i;<br /> idx = round2power(nbytes + sizeof(Header));<br /> /* Initilize the header block */<br /> if (free_buf == NULL)<br /> {<br /> free_buf = free_area[MAX_ORDER] = (Header *) buffer;<br /> free_area[MAX_ORDER]->s.next = NULL;<br /> free_area[MAX_ORDER]->s.prev = NULL;<br /> free_area[MAX_ORDER]->s.size = 1 << MAX_ORDER;<br /> }<br /> for (i = idx; i <= MAX_ORDER; i++)<br /> {<br /> if (free_area[i] != NULL)<br />{<br /> unsigned int j = i - 1;<br /> unsigned int size = 1 << j;<br /> Header *firstp, *secondp;<br /> for (firstp = free_area[i]; j >= idx; j--, size >>= 1)<br /> {<br /> secondp = (Header *)((char *)firstp + size);<br /> secondp->s.next = NULL;<br /> secondp->s.prev = NULL;<br /> secondp->s.size = size;<br /> insert(&free_area[j], secondp);<br /> }<br /> free_area[i] = firstp->s.next;<br /> p = firstp;<br /> p->s.next = NULL;<br /> p->s.prev = NULL;<br /> p->s.size = 1 << idx;<br /> return p + 1;<br />}<br /> }<br /> return NULL;<br />}<br />void buddy_free(void *bp)<br />{<br /> Header *prev, *p, *next;<br /> unsigned int idx;<br /> p = (Header *) bp - 1;<br /> idx = round2power(p->s.size);<br /> insert(&free_area[idx], p);<br /> prev = p->s.prev;<br /> next = p->s.next;<br /> /* check whether the buddies can be combined into the larger one */<br /> if (prev != NULL)<br /> {<br /> if (continous(prev, p))<br />{<br /> delete(&free_area[idx], p);<br /> delete(&free_area[idx], prev);<br /> prev = combine(prev, p);<br /> buddy_free((void *) (prev + 1));<br /> return;<br />}<br /> }<br /> if (next != NULL)<br /> {<br /> if (continous(p, next))<br />{<br /> delete(&free_area[idx], p);<br /> delete(&free_area[idx], next);<br /> next = combine(p, next);<br /> buddy_free((void *) (next + 1));<br /> return;<br />}<br /> }<br />}<br />void buddy_showblocks_test();<br />void buddy_showblocks()<br />{<br /> buddy_showblocks_test();<br /> unsigned int size = 1 << MIN_ORDER;<br /> int i;<br /> unsigned int bsize;<br /> Header *bp;<br /> char *ptr = buffer;<br /> char *buf_end = &buffer[BUF_SIZE - 1] + 1;<br /> for (bp = (Header *) ptr; ptr < buf_end; ptr += size, bp = (Header *)ptr)<br /> {<br /> bsize = bp->s.size;<br /> unsigned int tmpsize = bsize / size;<br /> int idx = round2power(bsize);<br /> Header *tp = free_area[idx];</p><p> while (tp != NULL)<br />if (tp == bp)<br /> break;<br />else<br /> tp = tp->s.next;<br /> if (tp != NULL)<br />{<br /> for (i = 0; i < tmpsize; i++)<br /> {<br /> printf("---/n");<br /> }<br />}<br /> else<br />{<br /> for (i = 0; i < tmpsize; i++)<br /> {<br /> printf("***/n");<br /> }<br />}</p><p> }<br />}<br />void buddy_showblocks_test()<br />{<br /> int i;</p><p> for (i = MIN_ORDER; i <= MAX_ORDER; i++)<br /> {<br /> if (free_area[i] != NULL)<br />{<br /> Header *tmp = free_area[i];<br /> int j;<br /> for (j = 0; tmp != NULL; j++, tmp = tmp->s.next)<br /> ;<br /> printf("%d has blocks: %d/n", i, j);<br />}<br /> else<br />printf("%d is empty./n", i);<br /> }<br />}

 

可以看到夥伴演算法對於零散塊的處理是比較好的。夥伴演算法相對於Next Fit演算法的好處是減少了External Fragmentation(外部碎塊,也就是在分配塊之間的洞),但是增加了Internal Fragmentation(內部碎塊,也就是在申請塊內部有一部分是沒有被用到的)。

 

經過測試,使用了100次隨機從20到120的隨機大小塊分配,然後再隨機的從這些分配塊裡面釋放。

得到的結果是Next Fit的片段數目是16,而Buddy的數目是11。

從這個數字多少可以感受到對於片段的處理,Buddy的確是優於Next Fit。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.