边界标识算法

前言

首先说明,我们这里的内存管理器主要是以模拟各种内存分配算法为主,从内存申请一片内存然后根据我们所选定的数据结构和算法,实现程序的堆空间的分配,至于内存分配详情我们会在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;

这个算法其他一些要注意的地方

  1. 假设每次需要找m 个大小,但是我们每次都分配n 给它,那么久而久之,就会有很多的m-n 个空间散落于链表中,所以我们需要设置一个标准值e,当m-n <= e 的时候,就将m 的空闲整块分配给它,反之,我们就分配想当需求大小的空间。
  2. 如果收每一次都从头开始寻找就是首次匹配,由于已经进行多次,必然前边会聚集较多的小块,所以我们应当每次分配一次就将,表头指向它已经分配的后边的一个节点,这样就能基本保证每一次的进行首次匹配的效果了。

回收算法:

回收的思想很简单,根据它前后块的不同,总共有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

内存管理器(二)边界标识法相关推荐

  1. 深入研究glibc内存管理器原理及优缺点

    最近查清了线上内存占用过大和swap使用频繁的原因:由于linux使用的glibc使用内存池技术导致的堆外内存暴增,基于这个过程中学习和了解了glibc的内存管理原理,和大家分享,如有错误请及时指出. ...

  2. 同样学习Linux, 为何差别这么大? - 论打通Linux进程和内存管理任督二脉

    穆赫兰道和内陆帝国 我在多年的工程生涯中发现很多工程师碰到一个共性的问题:Linux工程师很多,甚至有很多有多年工作经验,但是对一些关键概念的理解非常模糊,比如不理解CPU.内存资源等的真正分布,具体 ...

  3. 内存管理器剖析:ptmalloc,windows,macOS

    目录 1. Ptmalloc 2. Windows内存管理器 3. Mac OS内存管理器 4.推荐阅读 核心分析器的优势在于它能够将堆内存解析为数百万个单独的内存块,这些内存块由堆内存管理器分配给应 ...

  4. 大内高手 内存管理器

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 大内高手 ...

  5. 大内高手—内存管理器

    大内高手-内存管理器 转载时请注明出处和作者联系方式:http://blog.csdn.net/absurd 作者联系方式:李先静 <xianjimli at hotmail dot com&g ...

  6. C++性能优化(十一) —— 内存管理器性能分析

    一.PTMalloc2 1.PTMalloc2优点 (1)集成在glibc中,Linux主要发行版的通用实现. 2.PTMalloc2缺点 (1)后分配的内存先释放.由于ptmalloc2收缩内存是从 ...

  7. Spark存储体系——内存管理器

    Spark与Hadoop的重要区别之一就在于对内存的使用.Hadoop只将内存作为计算资源,Spark除将内存作为计算资源外,还将内存的一部分纳入到存储体系中.Spark使用MemeoryManage ...

  8. android之ION内存管理器(1)-- 简介

    by JHJ(jianghuijun211@gmail.com) 为什么需要ION 回顾2011年末[2],LWN审查了android kernel patch[3],以期望将这些patch合并到ke ...

  9. 内存管理器(十)kernel内存管理----数据结构

    内存管理器(十) kernel内存管理----概况与数据结构 前言 正式开始学习内核的内存管理了,先学习下接口函数,每一个例字都必须写内核模块实验,然后深入到函数的内部研究源码,最后写写练习的小程序. ...

最新文章

  1. swift 4.0 创建tableview 自定义cell
  2. [原] XAF How to Edit multiple objects in a ListViewAndDetailView
  3. asp.net mvc 3 RTM 发布了!
  4. idea开发swing项目_IntelliJ IDEA 开发swing(一)
  5. EL在java里的意义
  6. 云数据库·ApsaraDB 产品6月刊
  7. jframe运行和预览大小不一样_同一款车型,为什么配的轮胎大小还会不一样?【内含福利】...
  8. graphics | 基础绘图系统(八)——棘状图、符号图、多边形(路径)图、Cohen-Friendly关联图、条件密度图...
  9. WordPress 插件漏洞被利用,近 20 万站点还没打补丁
  10. linux网络分析之回环网卡
  11. CentOS7恢复rm -rf 误删的xfs系统
  12. CAD用直线绘制矩形
  13. 双击桌面计算机打不开硬盘,双击硬盘盘符打不开只能右键打开的解决方法 -电脑资料...
  14. CentOS下LVM的使用
  15. mongo数据库集合导出
  16. 1104. 天长地久 (20 分)
  17. 微信公众号红包营销系统开发
  18. 程序员面试100题之六 最长公共子序列
  19. java 支付宝帐单_java后台实现支付宝对账功能的示例代码
  20. Python开发技术—网络爬虫

热门文章

  1. phoenix php,Python实现连接phoenix的实例
  2. python namedtuple用法_Python的collections模块中namedtuple结构使用示例
  3. access update语句执行_MySQL 中 update 修改数据与原数据相同会再次执行吗?
  4. 数据库_数据库篇-第一章:数据库基本概念
  5. java需要记的语法,Java笔记(三)……基础语法
  6. PTA混淆总结(就做个笔记储存一下)
  7. jedis操作set_在Java中使用jedis操作Set类型
  8. centos 7 mysql界面管理器_centos7安装mysql5.7.24,并使用system管理mysql
  9. 泰安服务器维护公司,神云 泰安服务器
  10. C语言:随笔9--链表