背景知识-空间分配
- 实现动态分区的分配算法。
- 最佳适配算法选择内存空闲块中最适合进程大小的块分配。
- 首次适配算法从开始地址查找符合要求的块,所查找到的第一个满足要求的空闲块就分配给进程。
- 最差适配算法选择最大空闲块减少碎片。
背景知识-空间回收
- 相邻空闲区的合并问题,要求考虑四种情况:
- 释放区下邻空闲区(低地址邻接)
- 释放区上邻空闲区(高地址邻接)
- 释放区上下都与空闲区邻接
- 释放区上下邻都与空闲区不邻接
题目引入
假设初始状态下,可用的内存空间为640KB,并有下列的请求序列:
•作业1申请130KB
•作业2申请60KB
•作业3申请100KB
•作业2释放60KB
•作业4申请200KB
•作业3释放100KB
•作业1释放130KB
•作业5申请140KB
•作业6申请60KB
•作业7申请50KB
实验思路
数据结构
应用一个双向链表的结构来实现内存管理
先看链表节点类:
- freeTable是每个节点的数据,包括分区序号,起始地址,分区大小,分区状态
class freeTable
{
public:
int num; //分区序号
long address; //起始地址
long length; //分区大小
int state; //分区状态
freeTable(int num, long address, long length, int state);
};
- Node类是节点类,包含节点数据类freeTable,前后指针
#include "freeTable.h"
typedef freeTable ElemType;
class Node
{
public:
ElemType data;
Node* prior; //前趋指针
Node* next; //后继指针
Node(int num, long address, long length, int state);
Node(int num, long address, long length, int state, Node* pre,Node* nxt);
};
再看内存管理类:
MemoryManager类是用于模拟内存的一个类,用于对外提供接口来申请/释放接口,其本质是一个双向链表
class MemoryManager
{
private:
Node* head;
int nodeNum=1;
int maxLenth;
public:
MemoryManager(int _num, long _address, long _length, int _state);
MemoryManager(long _length);
void show();
int requestMemory(long memoryLen);//申请内存空间,返回申请到的内存空间地址,失败则返回-1
int bestFit(long memoryLen);//最佳适配算法
void freeMemory(int memoryNum);//释放内存
int firstFit(long memoryLen);//首次适配算法
int maxFit(long memoryLen);//最差适配算法
int allocateMemory(Node* reAllocatedNode, long memoryLen);//分配内存
};
假设操作系统本身就占用了40KB的容量,这一部分将被放在头结点中,所以构造函数如下:
MemoryManager::MemoryManager(int _num, long _address, long _length, int _state)
{
head = new Node(_num, _address, _length, _state);
}
MemoryManager::MemoryManager(long _length):MemoryManager(0,0,40,1)//假设操作系统本身占用40KB的容量
{
if (_length <= 40) {
cout << "分配的内存不足" << endl;
exit(0);
}
maxLenth = _length;
head->next = new Node(1, 40, maxLenth - 40, 0,head,0);
}
内存释放算法
内存的释放其实只需要将state置为0即可,但是考虑到相邻空闲区的合并问题,需要考虑四种情况:
- 释放区下邻空闲区(低地址邻接)
- 释放区上邻空闲区(高地址邻接)
- 释放区上下都与空闲区邻接
- 释放区上下邻都与空闲区不邻接
如上四种情况,综合下来只需要考虑两个相邻空闲区的合并问题,如合并下邻空闲区,合并上邻空闲区.事实上,这两个问题只需要用同一段代码实现即可,思路如下:
p与p的前驱节点合并
- 修改p前驱的data值
- 将p前驱的后继指向p的后继
- p后继的前驱指向p的前驱
综上可以将p独立出来,delete p即可
p->prior->data.length += p->data.length;
p->prior->next = p->next;
if (p->next) {
p->next->prior = p->prior;
}
delete p;
p与p的后继节点合并
这种情况只需要将p移动到p的后继节点,然后执行上面的算法即可
p与p的前驱节点和后继节点合并
这种情况只需要将p指向后继节点,然后两次执行上面的算法即可
综上所述,内存释放算法如下:
void MemoryManager::freeMemory(int memoryNum)
{
/// <summary>
/// 释放内存
/// </summary>
/// <param name="memoryNum">要释放的内存的序号</param>
if (memoryNum<=0) {
cout << "不可以释放该内存" << endl;
exit(1);
}
Node* p = head;
while (p) {
if (p->data.num == memoryNum) {
p->data.state = 0;
break;
}
p = p->next;
}
if (p == nullptr) {
cout << "未找到该内存空间" << endl;
exit(1);
}
/*内存释放完毕,下面开始合并上下内存
分四种情况
a.释放区下邻空闲区(低地址邻接)
b.释放区上邻空闲区(高地址邻接)
c.释放区上下都与空闲区邻接
d.释放区上下邻都与空闲区不邻接*/
if (p->next && p->next->data.state == 0) {//当释放区下邻空闲区(低地址邻接)
p = p->next;//将p下移
}
while (p->prior && p->prior->data.state == 0) {
//释放区上邻空闲区(高地址邻接)
//下面的操作会将p和p前面的空间合并,并且循环直到p前面不为空闲区
p->prior->data.length += p->data.length;
p->prior->next = p->next;
if (p->next) {
p->next->prior = p->prior;
}
Node* temp = p;
p = p->prior;
delete temp;
}
}
三个不同的内存分配算法
算法实现思路
下面的三个算法第一步是找出要分配的内存空间,第二步都是将内存空间开辟并分配内存,因为第二步三个算法都是一样的实现过程,所以我们可以将代码抽取出来封装在一个函数中,代码如下
内存空间分配算法
大致思路是
如要分配Node2的空间
- 新建一个节点NewNode,设置节点NewNode的data值,然后改变NewNode的前后指针
- 然后改变Node2的前驱节点的next指向和Node2节点的pre指向
int MemoryManager::allocateMemory(Node* reAllocatedNode,long memoryLen)
{
if (reAllocatedNode && reAllocatedNode->data.length < memoryLen) {
cout << "内存不足以分配" << endl;
exit(1);
}
//开始分配
//这块指针节点将被分配成两部分,第一部分为被分配的空间,第二部分为未分配的空间
nodeNum++;
if (reAllocatedNode->data.length == memoryLen) {
//如果被分配的空间大小和申请的空间大小相同,则直接改变状态即可
reAllocatedNode->data.state = 1;
reAllocatedNode->data.num = nodeNum;
return nodeNum;
}
long nodeAddress = reAllocatedNode->data.address;
Node* newNode = new Node(nodeNum, nodeAddress, memoryLen, 1, reAllocatedNode->prior, reAllocatedNode);
//指针指向改变
reAllocatedNode->data.address += memoryLen;
reAllocatedNode->data.length -= memoryLen;
reAllocatedNode->prior->next = newNode;
reAllocatedNode->prior = newNode;
return nodeNum;
}
而第一步就根据算法的思想来找到要分配的内存空间
最佳适配算法
最佳适配算法选择内存空闲块中最适合进程大小的块分配
先遍历链表找出空间最大的空闲块,然后再遍历一次链表,根据前面找到的最大的空间块来找到满足最小的空间要求的空闲块,然后执行内存空间分配算法即可
代码如下:
int MemoryManager::bestFit(long memoryLen)
{
/// <summary>
/// //最佳适配算法,选择内存空闲块中最适合进程大小的块分配
/// 申请内存空间,返回申请到的内存空间地址,失败则返回-1
/// </summary>
/// <param name="memoryLen">要申请的内存长度</param>
/// <returns></returns>
Node* p = head->next,*reAllocateMemoryNode=p;
while (p) {
if (p->data.state==0) {
if (p->data.length >= reAllocateMemoryNode->data.length) {
reAllocateMemoryNode = p;
}
}
p = p->next;
}
p = head;
while (p) {
if (p->data.state==0) {//空间空闲
if (p->data.length > memoryLen && p->data.length <= reAllocateMemoryNode->data.length) {
//当前指针节点的空间合适且比上次定位的指针节点空间小
//则改变定位指针的位置
reAllocateMemoryNode = p;
}
}
p = p->next;
}
return allocateMemory(reAllocateMemoryNode, memoryLen);
}
首次适配算法
首次适配算法从开始地址查找符合要求的块,所查找到的第一个满足要求的空闲块就分配给进程。
遍历一次链表直到第一次找到满足空间要求的空闲块
int MemoryManager::firstFit(long memoryLen)
{
Node* reAllocateMemoryNode = head->next;
while (reAllocateMemoryNode) {//找到第一个适配的内存空间
if (reAllocateMemoryNode->data.state == 0) {
if (reAllocateMemoryNode->data.length >= memoryLen) {
break;
}
}
reAllocateMemoryNode = reAllocateMemoryNode->next;
}
return allocateMemory(reAllocateMemoryNode, memoryLen);
}
最差适配算法
最差适配算法选择最大空闲块减少碎片
遍历一次找到最大的空间块即可
int MemoryManager::maxFit(long memoryLen)
{
Node* p = head->next, * reAllocateMemoryNode = p;
while (p) {
if (p->data.state == 0) {
if (p->data.length >= reAllocateMemoryNode->data.length) {
reAllocateMemoryNode = p;
}
}
p = p->next;
}
return allocateMemory(reAllocateMemoryNode, memoryLen);
}
运算结果
主函数如下
int main()
{
MemoryManager memoryManager(640);
memoryManager.show();
cout << "作业1申请130KB" << endl;
int first = memoryManager.requestMemory(130);memoryManager.show();
cout << "作业2申请60KB" << endl;
int second = memoryManager.requestMemory(60);memoryManager.show();
cout << "作业3申请100KB" << endl;
int third = memoryManager.requestMemory(100);memoryManager.show();
cout << "作业2释放60KB" << endl;
memoryManager.freeMemory(second); memoryManager.show();
cout << "作业4申请200KB" << endl;
int four = memoryManager.requestMemory(200);memoryManager.show();
cout << "作业3释放100KB" << endl;
memoryManager.freeMemory(third); memoryManager.show();
cout << "作业1释放130KB" << endl;
memoryManager.freeMemory(first); memoryManager.show();
cout << "作业5申请140KB" << endl;
int five = memoryManager.requestMemory(140); memoryManager.show();
cout << "作业6申请60KB" << endl;
int six = memoryManager.requestMemory(60); memoryManager.show();
cout << "作业7申请50KB" << endl;
int seven = memoryManager.requestMemory(70); memoryManager.show();
}