内存管理器(二)边界标识法
生活随笔
收集整理的這篇文章主要介紹了
内存管理器(二)边界标识法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
邊界標識算法
前言
首先說明,我們這里的內存管理器主要是以模擬各種內存分配算法為主,從內存申請一片內存然后根據我們所選定的數據結構和算法,實現程序的堆空間的分配,至于內存分配詳情我們會在Linux內核內存管理的簡單分析中探討。
這個算法是什么
邊界標識法是操作系統中用以進行動態分配的一種存儲管理的方法,系統將所有的空閑塊鏈接在一個雙重循環鏈表結構中;分配可以按照首次匹配,最佳匹配方法執行,其次個人覺得先學這個算法然后在學伙伴算法能更簡單點吧。
這個算法的特點
在每個內存區的頭部和底部兩個邊界上分別設有標識,以標識該區域的占用塊和空閑塊,使得在回收用戶釋放的空閑塊時易于判別在物理上位置上與其相鄰的內存區域是否為空閑塊,以便將所有地址連續的空閑存儲區組合成一個盡可能更大的空閑塊。
圖解這個算法
這就是抽象的鏈表節點,分別由頭head,空間(內存塊),尾tail ,組成。
其中表頭由4個部分組成。
llink : 這個數據結構主體是循環鏈表,所以這個指針指向前一個節點。
tag : 標示位:1標示為已分配塊,0標示未分配塊。
size : 標示這個節點的大小(包括頭部和尾部)。
rlink : 由于是雙向循環鏈表,這個指針指向后一個節點。
表尾由2個部分組成:
uplink : 指向本節點的頭部。
tag : 同上標示分配情況。
其中這些節點的鏈接形式如圖所示,基礎是雙向循環鏈表并且包含上述結構體。
節點數據結構
typedef struct WORD{ //WORD :內存字類型union{WORD *llink ; //頭和尾都是同一個節點WORD *uplink;};int tag ; //塊標志,0:空閑,1:占用int size; //塊大小WORD *rlink; //指向后繼節點OtherType other; //其它部分}WORD,head,foot,*Space; 這個算法其他一些要注意的地方
- 假設每次需要找m 個大小,但是我們每次都分配n 給它,那么久而久之,就會有很多的m-n 個空間散落于鏈表中,所以我們需要設置一個標準值e,當m-n <= e 的時候,就將m 的空閑整塊分配給它,反之,我們就分配想當需求大小的空間。
- 如果收每一次都從頭開始尋找就是首次匹配,由于已經進行多次,必然前邊會聚集較多的小塊,所以我們應當每次分配一次就將,表頭指向它已經分配的后邊的一個節點,這樣就能基本保證每一次的進行首次匹配的效果了。
回收算法:
回收的思想很簡單,根據它前后塊的不同,總共有4中情況。
1.前后都已經占用
直接將內存塊插入。
2.前一個已經被占用,后一個沒有被占用。
我們直接將后一個,和待回收的塊合并成一個完整的塊。
3前一個沒有被占用,后一個已經被占用。
我們將前一個和待回收的塊合并成一個完整的塊。
4前一個與后一個都沒有被占用。
我們將三個塊全部合并成一個完整的未分配塊。
下面就來看一個使用邊界標識法的空間管理簡例
#include<stdio.h>
#include<stdlib.h>#define MAXSIZE 1000
#define ALLOC_MIN 100
#define FootLoc(p) (p+(p->size) - 1)typedef struct WORD{ //WORD:內存字類型union { //頭和尾都指向同一個位置使用聯合體struct WORD *llink;struct WORD *uplink;};int tag ; //塊標識:1:占用 0: 空閑int size ; //塊的大小struct WORD *rlink; //指向后繼節點// OtherType other; //字的其他部分這里暫時用不到}*Space;Space user[MAXSIZE] = {NULL} ; //用戶空間數組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){ // 找到可以分配的塊開始分配 ,同樣也為了減少碎片 ,從高位截取p,且設置新的底部p->size -= n; //計算剩余塊的大小Space foot = FootLoc(p); //指向剩余塊的底部foot->uplink = p; //設置剩余塊的底部foot->tag = 0 ; //設置剩余塊底部的標識p = foot + 1 ; //指向分配塊的頭部p->size = n ; //設置分配塊的頭部foot = FootLoc(p); //指向分配塊的底部p->tag = 1 ; //設置分配塊的頭部foot ->tag = 1; //同上foot->uplink = p ;}else{ //分配后剩余空間小于規定的ALLOC_MINif(p == *pav){ //如果只剩下一個空間了,清空指針*pav = NULL ;}else{ //直接分配整個塊出去,雖然會浪費一部分空間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; //設置頭指針標示符head++; //頭指針指向第一個節點head->tag = 0; //設置第一個節點為空閑塊 head->llink = head->rlink = head; //設置循環鏈表head->size = MAXSIZE ; //設置塊的大小*pav = head ; //設置頭指針 Space foot = FootLoc(head); foot->tag = 0;foot->uplink = head ;foot++;foot->tag = 1; //設置尾邊界為已經使用}void reclaimBoundTag(Space *pav ,Space sp){Space pre = (sp - 1)->uplink ;Space next = sp + sp->size ;int pTag = pre->tag ;int nTag = next->tag ; //聲明兩個節點,分別得到前一個和后一個節點的信息,并且記錄占用情況,根據占用情況選擇合并的手段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{ //三個塊一起合并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》
《數據結構(嚴尉敏)》
http://blog.csdn.net/fuming0210sc/
版權聲明:本文為博主原創文章,未經博主允許不得轉載。
轉載于:https://www.cnblogs.com/zmrlinux/p/4921381.html
總結
以上是生活随笔為你收集整理的内存管理器(二)边界标识法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 输卵造影多少钱啊?
- 下一篇: Android环境变量的设置(详细图解版