内存管理器(二)边界标识法
邊界標(biāo)識(shí)算法
前言
首先說(shuō)明,我們這里的內(nèi)存管理器主要是以模擬各種內(nèi)存分配算法為主,從內(nèi)存申請(qǐng)一片內(nèi)存然后根據(jù)我們所選定的數(shù)據(jù)結(jié)構(gòu)和算法,實(shí)現(xiàn)程序的堆空間的分配,至于內(nèi)存分配詳情我們會(huì)在Linux內(nèi)核內(nèi)存管理的簡(jiǎn)單分析中探討。
這個(gè)算法是什么
邊界標(biāo)識(shí)法是操作系統(tǒng)中用以進(jìn)行動(dòng)態(tài)分配的一種存儲(chǔ)管理的方法,系統(tǒng)將所有的空閑塊鏈接在一個(gè)雙重循環(huán)鏈表結(jié)構(gòu)中;分配可以按照首次匹配,最佳匹配方法執(zhí)行,其次個(gè)人覺(jué)得先學(xué)這個(gè)算法然后在學(xué)伙伴算法能更簡(jiǎn)單點(diǎn)吧。
這個(gè)算法的特點(diǎn)
在每個(gè)內(nèi)存區(qū)的頭部和底部?jī)蓚€(gè)邊界上分別設(shè)有標(biāo)識(shí),以標(biāo)識(shí)該區(qū)域的占用塊和空閑塊,使得在回收用戶釋放的空閑塊時(shí)易于判別在物理上位置上與其相鄰的內(nèi)存區(qū)域是否為空閑塊,以便將所有地址連續(xù)的空閑存儲(chǔ)區(qū)組合成一個(gè)盡可能更大的空閑塊。
圖解這個(gè)算法
這就是抽象的鏈表節(jié)點(diǎn),分別由頭head,空間(內(nèi)存塊),尾tail ,組成。
其中表頭由4個(gè)部分組成。
llink : 這個(gè)數(shù)據(jù)結(jié)構(gòu)主體是循環(huán)鏈表,所以這個(gè)指針指向前一個(gè)節(jié)點(diǎn)。
tag : 標(biāo)示位:1標(biāo)示為已分配塊,0標(biāo)示未分配塊。
size : 標(biāo)示這個(gè)節(jié)點(diǎn)的大小(包括頭部和尾部)。
rlink : 由于是雙向循環(huán)鏈表,這個(gè)指針指向后一個(gè)節(jié)點(diǎn)。
表尾由2個(gè)部分組成:
uplink : 指向本節(jié)點(diǎn)的頭部。
tag : 同上標(biāo)示分配情況。
其中這些節(jié)點(diǎn)的鏈接形式如圖所示,基礎(chǔ)是雙向循環(huán)鏈表并且包含上述結(jié)構(gòu)體。
節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)
typedef struct WORD{ //WORD :內(nèi)存字類型union{WORD *llink ; //頭和尾都是同一個(gè)節(jié)點(diǎn)WORD *uplink;};int tag ; //塊標(biāo)志,0:空閑,1:占用int size; //塊大小WORD *rlink; //指向后繼節(jié)點(diǎn)OtherType other; //其它部分}WORD,head,foot,*Space; 這個(gè)算法其他一些要注意的地方
- 假設(shè)每次需要找m 個(gè)大小,但是我們每次都分配n 給它,那么久而久之,就會(huì)有很多的m-n 個(gè)空間散落于鏈表中,所以我們需要設(shè)置一個(gè)標(biāo)準(zhǔn)值e,當(dāng)m-n <= e 的時(shí)候,就將m 的空閑整塊分配給它,反之,我們就分配想當(dāng)需求大小的空間。
- 如果收每一次都從頭開(kāi)始尋找就是首次匹配,由于已經(jīng)進(jìn)行多次,必然前邊會(huì)聚集較多的小塊,所以我們應(yīng)當(dāng)每次分配一次就將,表頭指向它已經(jīng)分配的后邊的一個(gè)節(jié)點(diǎn),這樣就能基本保證每一次的進(jìn)行首次匹配的效果了。
回收算法:
回收的思想很簡(jiǎn)單,根據(jù)它前后塊的不同,總共有4中情況。
1.前后都已經(jīng)占用
直接將內(nèi)存塊插入。
2.前一個(gè)已經(jīng)被占用,后一個(gè)沒(méi)有被占用。
我們直接將后一個(gè),和待回收的塊合并成一個(gè)完整的塊。
3前一個(gè)沒(méi)有被占用,后一個(gè)已經(jīng)被占用。
我們將前一個(gè)和待回收的塊合并成一個(gè)完整的塊。
4前一個(gè)與后一個(gè)都沒(méi)有被占用。
我們將三個(gè)塊全部合并成一個(gè)完整的未分配塊。
下面就來(lái)看一個(gè)使用邊界標(biāo)識(shí)法的空間管理簡(jiǎn)例
#include<stdio.h>
#include<stdlib.h>#define MAXSIZE 1000
#define ALLOC_MIN 100
#define FootLoc(p) (p+(p->size) - 1)typedef struct WORD{ //WORD:內(nèi)存字類型union { //頭和尾都指向同一個(gè)位置使用聯(lián)合體struct WORD *llink;struct WORD *uplink;};int tag ; //塊標(biāo)識(shí):1:占用 0: 空閑int size ; //塊的大小struct WORD *rlink; //指向后繼節(jié)點(diǎn)// OtherType other; //字的其他部分這里暫時(shí)用不到}*Space;Space user[MAXSIZE] = {NULL} ; //用戶空間數(shù)組int usCount = 0;Space AllocBoundTag(Space *pav,int n){Space p = * pav;if(NULL == p){printf("The memory is NULL \n");return NULL;}for(p;p != NULL && p->size < n && p->rlink != *pav; p = p->rlink ){if(p == NULL || p->size < n){printf("error is :%d\n",__LINE__);return NULL;}*pav = p->rlink; //防止小的零碎空間全部集中在前邊if(p->size - n > ALLOC_MIN){ // 找到可以分配的塊開(kāi)始分配 ,同樣也為了減少碎片 ,從高位截取p,且設(shè)置新的底部p->size -= n; //計(jì)算剩余塊的大小Space foot = FootLoc(p); //指向剩余塊的底部foot->uplink = p; //設(shè)置剩余塊的底部foot->tag = 0 ; //設(shè)置剩余塊底部的標(biāo)識(shí)p = foot + 1 ; //指向分配塊的頭部p->size = n ; //設(shè)置分配塊的頭部foot = FootLoc(p); //指向分配塊的底部p->tag = 1 ; //設(shè)置分配塊的頭部foot ->tag = 1; //同上foot->uplink = p ;}else{ //分配后剩余空間小于規(guī)定的ALLOC_MINif(p == *pav){ //如果只剩下一個(gè)空間了,清空指針*pav = NULL ;}else{ //直接分配整個(gè)塊出去,雖然會(huì)浪費(fèi)一部分空間Space foot = FootLoc(p);foot->tag = p->tag = 1;p->llink->rlink = p->rlink ;p->rlink->llink = p->llink ;}}}user[usCount++] = p; return p;
}void Space_init(Space * freeSpace, Space *pav){*freeSpace = (Space)malloc(sizeof(struct WORD)*(MAXSIZE + 2)); //初始化空間鏈表Space head = *freeSpace ; //頭指針head->tag = 1; //設(shè)置頭指針標(biāo)示符head++; //頭指針指向第一個(gè)節(jié)點(diǎn)head->tag = 0; //設(shè)置第一個(gè)節(jié)點(diǎn)為空閑塊 head->llink = head->rlink = head; //設(shè)置循環(huán)鏈表head->size = MAXSIZE ; //設(shè)置塊的大小*pav = head ; //設(shè)置頭指針 Space foot = FootLoc(head); foot->tag = 0;foot->uplink = head ;foot++;foot->tag = 1; //設(shè)置尾邊界為已經(jīng)使用}void reclaimBoundTag(Space *pav ,Space sp){Space pre = (sp - 1)->uplink ;Space next = sp + sp->size ;int pTag = pre->tag ;int nTag = next->tag ; //聲明兩個(gè)節(jié)點(diǎn),分別得到前一個(gè)和后一個(gè)節(jié)點(diǎn)的信息,并且記錄占用情況,根據(jù)占用情況選擇合并的手段if(pTag == 1 && nTag == 1 ){ //前后都是滿的直接插入Space foot = FootLoc(sp);foot->tag = sp->tag = 0;if(pav == NULL){*pav = sp->llink = sp->rlink = sp;}else{sp->rlink = *pav ;sp->llink = (*pav)->llink;(*pre).llink = sp ;sp->llink->rlink = sp;*pav = sp;}}else if(pTag == 0 && nTag == 1){ // 前邊的可以合并pre->size += sp->size ;Space foot = FootLoc(pre);foot->tag = 0;foot->uplink = pre;}else if(pTag == 1 && nTag == 0){ //后邊的可以合并sp->llink = next->llink;sp->rlink = next->rlink;next->llink->rlink = sp ;next->rlink->llink = sp ;sp->size += next->size ;Space foot = FootLoc(sp);sp->tag = foot->tag = 0 ;foot->uplink = sp;}else{ //三個(gè)塊一起合并pre->rlink = next->rlink;pre->size += sp->size + next->size;Space foot = FootLoc(pre);foot->uplink = pre ;}int i ;for(i = 0;i < usCount ;i++){if(sp == user[i]){user[i] = NULL;}}
}void print(Space s){printf("The head is %0x SIZE: %d \n pre is %0x ,next is %0x\n",s,s->size,s->llink,s->rlink);
}void print_space(Space pav){if(pav != NULL){printf("you an use the list:\n");Space pos = pav;for(pos = pos->rlink;pos != pav;pos = pos->rlink){print(pos);}}printf("_____________________________\n");int i ;for(i = 0;i< usCount ;i++){Space us = user[i];if(us){printf("the head is %0x SIZE is %d\n",us,us->size);}}}int main(){Space freeSpace = NULL;Space pav = NULL;Space_init(&freeSpace,&pav);print(pav);printf("malloc a 300 and 300 \n");Space m3 = AllocBoundTag(&pav,300);print_space(pav);Space t3 = AllocBoundTag(&pav,300);print_space(pav);printf("free 300 \n");reclaimBoundTag(&pav,m3);print_space(pav);return 0;
}
參考資料:
《CSAPP》
《數(shù)據(jù)結(jié)構(gòu)(嚴(yán)尉敏)》
http://blog.csdn.net/fuming0210sc/
版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。
轉(zhuǎn)載于:https://www.cnblogs.com/zmrlinux/p/4921381.html
總結(jié)
以上是生活随笔為你收集整理的内存管理器(二)边界标识法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 输卵造影多少钱啊?
- 下一篇: Android环境变量的设置(详细图解版