欢迎使用CSDN-markdown编辑器
来源:互联网 发布:网强网络管理系统 编辑:程序博客网 时间:2024/04/30 02:04
对于伙伴系统而言,有大小固定且相同的内存块,将大小相同的内存块链接在一起时,为了达到写平衡,使用双向链表实现,这样可以在回收回的内存块时,将其插入双向链表的链尾,在申请内存块时,从链头删除内存块;而将链接不同大小内存块的双向链表链接起来时,因为内存块大小种类固定所以使用顺序表实现,故使用顺序表加双向链表的形式实现伙伴系统。
双向链表设计思路及定义
细化而言,对于内存块,因为是在双向链表中,所以每个内存块中需要一个header,其中包含的信息有前驱 llink、后继 rlink;同时还需要一个标记内存块是否被占用的信息 tag,tag = 0 则内存块空闲,tag = 1则内存块占用;需要一个标记内存块大小的信息 kval,内存块大小为
所以我们在实现内存池时,以一个header的大小16个字节为单位进行内存管理,每个单位称之为WORD.
typedef struct WORD{ struct WORD *llink; int tag; int k; struct WORD *rlink;}WORD, *Space;
顺序表设计思路及定义
对于顺序表,则需要一个记录内存块大小的信息 nodesize;一个记录链接与 nodesize 大小相同的内存块的双向链表的结点信息 first。
typedef struct HNode{ int size; WORD *next;}HNode, *PHNode;
实现动态内存管理
内存初始化
通过初始化实现如上图所示的内存池。同时在这里需要建立一个内存池头结点、全局变量 HEAD 记录内存池首地址,作用下文会有详细说明。
void InitMemory(PHNode *ppav) /*初始化:创建一个需要进行内存管理的内存池*/{ WORD word[SIZE]; HNode head[K]; HEAD = word;//保存内存池首地址 word[0].llink = word;//初始化内存池 word[0].tag = 0; word[0].k = K - 1; word[0].rlink = word; *ppav = head;//将顺序表链接到头指针 int i; int size = 1; for(i = 0; i < K; i++, size *= 2)//初始化顺序表 { head[i].size = size; head[i].next = NULL; } head[K - 1].next = word;//将内存池链接到顺序表}
动态内存申请
WORD *Mymalloc(PHNode ppav, int size) /*动态内存申请*/{ assert(ppav != NULL); if (ppav == NULL) { return NULL; } int i; //int j = -1; for (i = 0; i < K; i++) { //if (ppav[i].size >= size && j == -1) //{ // j = i; //} if (ppav[i].next != NULL && ppav[i].size >= size)//判断是否有足够剩余空间 { break; } } if (i == K)//剩余空间不足 { return NULL; } Space q; Space head = ppav[i].next; Space p = head; //有剩余空间 if (head->llink == head)//该双向链表中只有一个节点 { //head = NULL; Error: 无法改变ppav[i].next的值,只改变head的值,函数运行完成后销毁,无用 ppav[i].next = NULL; } else//该双向链表不只有一个节点 { ppav[i].next = head->rlink; head->llink->rlink = head->rlink; head->rlink->llink = head->llink; } /*方法一: 从左向右由小到大切割内存块 q = p + ppav[j].size; p->llink = p; p->k = j; p->tag = 1; p->rlink = p; while (j < i) { q->llink = q; q->tag = 0; q->k = j; q->rlink = q; ppav[j].next = q; j++; q = q + ppav[j].size; } */ /*方法二: 从右向左有大到小切割内存块*/ for(i--; ppav[i].size >= size; i--)//大块内存块切割小块内存块 { q = p + ppav[i].size; q->llink = q; q->tag = 0; q->k = i; q->rlink = q; ppav[i].next = q; } p->llink = p; p->tag = 1; p->k = i + 1; p->rlink = p; return p;}
动态内存申请时要注意:
- 当双向链表值在顺序表中的头指针为NULL的情况
- 双向链表只有一个节点的情况
动态内存申请的难点在于处理大块内存块切割小块内存块的情况,这时有两种方法,方法一: 从左向右由小到大切割内存块,方法二: 从右向左有大到小切割内存块。
动态内存回收
void Myfree(PHNode ppav, WORD *p) /*动态内存回收*/{ p->tag = 0; int i; int k = p->k; int size = powx(2, k); Space q; int flag; while(k < K) { if ((p - HEAD) % (size * 2) == 0)//判断左块、右块 { flag = 0;//左块 q = p + size; } else { flag = 1;//右块 q = p - size; } if (q->tag == 0 && flag == 1 && q->k == p->k)//p为右块且有p的左块存在 { if(q->rlink == q) { ppav[k].next = NULL; } else { ppav[k].next = q->rlink;//易错: 若p的左块为ppav[k].next所指的空间块,不写则在输出时会陷入死循环 q->llink->rlink = q->rlink; q->rlink->llink = q->llink; } q->k++; p = q; k++; } else if (q->tag == 0 && flag == 0 && q->k == p->k)//p位左块且有p的右块存在 { if(q->rlink == q) { ppav[k].next = NULL; } else { ppav[k].next = q->rlink;//易错: 若p的左块为ppav[k].next所指的空间块,不写则在输出时会陷入死循环 q->llink->rlink = q->rlink; q->rlink->llink = q->llink; } p->k++; k++; } else//p没有伙伴块 { break; } size *= 2; } if(ppav[p->k].next == NULL) { p->llink = p; p->rlink = p; ppav[p->k].next = p; } else { q = ppav[k].next; q->llink->rlink = p;//将p插入内存池 p->llink = q->llink; q->llink = p; p->rlink = q; }}
在动态内存回收中需要注意:
- 在判断回收的内存块的伙伴是否空闲时应使用q->tag == 0 && flag == 0 && q->k == p->k进行判断,缺一不可
- 代码第33、50行不可缺少
- 当双向链表值在顺序表中的头指针为NULL的情况
- 双向链表只有一个节点的情况
动态内存回收的难点在于内存回收时对于伙伴块的合并,应使用循环,当回收的内存块有伙伴时,与伙伴合并成为新的内存块,新的内存块再次查看是否有伙伴,直至新的内存块没有伙伴,将其插入和其大小的匹配的双向链表中,正如整体设计中所说,这时双向链表的价值就体现出来,为了写平衡,双向链表可轻易将新内存块插入双向链表的链表尾。
在这其中伙伴是一个很重要的概念,何为“伙伴”?将一个大块内存切割成为两个大小相同的小块内存,两个小块内存互为伙伴。
在寻找回收的内存块的伙伴时,首先需要知道回收的内存块是左块还是右块,pMOD2k+1=0 则为左块,否则为右块。这里的 p 指回收的内存块相对于内存池首地址的相对地址,这时在内存初始化中的HEAD变量的作用就体现出来。
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 算法训练之BFS POJ 2251 Dungeon Master 三维最短路径
- 算法-排序-快速排序
- Python学习-02
- MUI 下拉刷新
- 怎样编写高质量的Java代码
- 欢迎使用CSDN-markdown编辑器
- 场景化学习 git
- Java调用weka的各种聚类算法
- jsonp跨域
- 八步快速将本地的工程通过Git上传到GitHub
- java Servlet(六)文件上传
- 写给18岁的自己
- 修改Win7开机登录界面背景图片
- ionic2 html