操作系统内存分配算法

背景知识-空间分配

  1. 实现动态分区的分配算法。
  2. 最佳适配算法选择内存空闲块中最适合进程大小的块分配。
  3. 首次适配算法从开始地址查找符合要求的块,所查找到的第一个满足要求的空闲块就分配给进程。
  4. 最差适配算法选择最大空闲块减少碎片。

背景知识-空间回收

  1. 相邻空闲区的合并问题,要求考虑四种情况:
    1. ​释放区下邻空闲区(低地址邻接)
    2. ​释放区上邻空闲区(高地址邻接)
    3. ​释放区上下都与空闲区邻接
    4. 释放区上下邻都与空闲区不邻接

题目引入

假设初始状态下,可用的内存空间为640KB,并有下列的请求序列:
•作业1申请130KB
•作业2申请60KB
•作业3申请100KB
•作业2释放60KB
•作业4申请200KB
•作业3释放100KB
•作业1释放130KB
•作业5申请140KB
•作业6申请60KB
•作业7申请50KB

实验思路

数据结构

应用一个双向链表的结构来实现内存管理
先看链表节点类:

  1. freeTable是每个节点的数据,包括分区序号,起始地址,分区大小,分区状态
class freeTable
{
public:
    int num; //分区序号
    long address; //起始地址
    long length;   //分区大小
    int state;   //分区状态
    freeTable(int num, long address, long length, int state);
};
  1. 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的前驱节点合并

  1. 修改p前驱的data值
  2. 将p前驱的后继指向p的后继
  3. p后继的前驱指向p的前驱

image.png
综上可以将p独立出来,delete p即可
image.png

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的空间

  1. 新建一个节点NewNode,设置节点NewNode的data值,然后改变NewNode的前后指针

image.png

  1. 然后改变Node2的前驱节点的next指向和Node2节点的pre指向

image.png

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();
}

最佳适配算法

J{@M62GYW%CH@K9@FM{V~7L.png

首次适配算法

请添加图片描述

最差适配算法

image.png

源代码点我

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值