内存管理器(二)边界标识法
2015-10-12 22:54
190 查看
边界标识算法
前言
首先说明,我们这里的内存管理器主要是以模拟各种内存分配算法为主,从内存申请一片内存然后根据我们所选定的数据结构和算法,实现程序的堆空间的分配,至于内存分配详情我们会在Linux内核内存管理的简单分析中探讨。
这个算法是什么
边界标识法是操作系统中用以进行动态分配的一种存储管理的方法,系统将所有的空闲块链接在一个双重循环链表结构中;分配可以按照首次匹配,最佳匹配方法执行,其次个人觉得先学这个算法然后在学伙伴算法能更简单点吧。
这个算法的特点
在每个内存区的头部和底部两个边界上分别设有标识,以标识该区域的占用块和空闲块,使得在回收用户释放的空闲块时易于判别在物理上位置上与其相邻的内存区域是否为空闲块,以便将所有地址连续的空闲存储区组合成一个尽可能更大的空闲块。
图解这个算法
![](http://img3.imgtn.bdimg.com/it/u=954868016,2716351735&fm=15&gp=0.jpg)
这就是抽象的链表节点,分别由头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_MIN if(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/
相关文章推荐
- 应用领航:盘点那些年我们一起追过的OS
- 无奇不有!盘点各国自己开发的操作系统
- Lua的内存管理浅析
- Lua教程(七):数据结构详解
- 可自定义oem的萝卜家园 Ghost XP 新春装机版 V200801 下载
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构揭秘一
- C#实现判断操作系统是否为Win8以上版本
- 数据结构之Treap详解
- 解析C语言中位字段内存分配的问题
- C语言编程中分配内存空间的相关函数
- C#字符串内存分配与驻留池学习分享
- JavaScript数据结构和算法之图和图算法
- Linux操作系统添加新硬盘方法
- java如何获取本地操作系统进程列表
- 基于C++内存分配、函数调用与返回值的深入分析
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- Linux rdesktop操作系统下远程登录Windows XP桌面
- 32位操作系统认出超出4G内存的方法
- Linux rpm tar 操作系统下软件的安装与卸载方法