本篇实现的程序都是使用C/C++代码实现的,在Linux和Windows平台下都可以运行,代码具有可移植性。在动态分区内存分配算法中,one.cc代码对应的是首次适应算法,two.cc代码对应的是循环首次适应算法。在基于索引分区的内存分配算法中,one.cc对应的是一个固定的案例,two.cc对应的是一个随机数产生的案例。虚拟内存页面置换算法中则只有一个lur.cc文件对应虚拟内存页面置换算法。
文章目录
动态分区内存分配算法
本篇实现的程序都是使用C/C++代码实现的,在Linux和Windows平台下都可以运行,代码具有可移植性。在动态分区内存分配算法中,one.cc代码对应的是首次适应算法,two.cc代码对应的是循环首次适应算法。在基于索引分区的内存分配算法中,one.cc对应的是一个固定的案例,two.cc对应的是一个随机数产生的案例。虚拟内存页面置换算法中则只有一个lur.cc文件对应虚拟内存页面置换算法。
设计任务
分配
假设内存总大小为1024,开始地址为0,结束地址为1023.有10个进程,它们所需的内存大小随机在[100, 200]之间产生。从第一个进程开始,使用指定的分区分配算法依次为进程分配内存分区。
若还有足够大的空闲分区,则给该进程进行分配,并更新空闲分区链表。当对一块空闲分区进行划分时,在这块空闲的地址范围内随机产生一个划分的开始位置,然后划分出当前进程大小的分区。
若没有足够大的空闲分区,则提示内存分配失败,然后继续为下一个进程分配空闲内存分区,知道10个进程都处理完毕。
(1)实现动态分区分配的首次适应算法(FF)
(2)实现动态分区分配的循环首次适应算法(NF)。
回收
10个进程处理完内存分配之后,执行内存回收过程,依次从第一个成功分配了内存分区的进程开始,回收其占用的内存分区,知道所有被占用的分区都回收完毕。
要求
- 在内存分配过程中,输出每一次内存分配结果:即成功还是失败,还有空闲/占用分区块情况
- 在内存回收过程中,输出每一次内存回收后的结果:即空闲/占用分区块情况。
实现
先介绍总体思路:一共存在三个list容器和一个变量。三个容器:freeblocks:存储空闲分区块、usedblocks:存放被使用过的内存块、pcbs:用于存放所有的进程。一个变量:blockID:表示当前分配出去内存块的id。
首先在freeblocks中插入一个起始地址为0,内存大小为1024的内存块,同时使用随机数生成[100, 200]之间数代表进程所需的内存大小,循环十次,生成10个进程,然后放入到pcbs容器中,pcbs中的进程id从0开始递增。
当找到大小足够的内存块之后,给pcb分配内存的时候会出现三种情况:
- 将内存块整个分配给pcb:当前内存块的大小和pcb所需内存大小刚好一致。
- 将内存块分成两块:给pcb分配的起始地址和分配内存的起始地址一样(分配左区间)、或pcb的起始地址加上他所需的内存刚好等于内存块的右区间(分配右区间)
- 将内存块分成三块:pcb的起始地址不等于内存块起始地址且pcb起始地址加上所需内存不等于内存块的右区间。
找到需要分配出去的内存块之后,需要注意给内存块的id字段按照blockID变量进行分配。
对于在内存的分配过程中,输出每次内存分配结果,空闲/占用分区的情况,回收后空闲/占用分区的情况都使用特定的格式循环打印即可。
首次适应算法
思路:循环遍历每一个pcb,并给他们分配对应的内存。每一次分配内存都是从低地址开始循环查找是否存在合适大小的内存块,一旦找到大小足够的内存块则跳出循环(若找不到则将将该pcb的status标志位设置为-1,找到了则设置为1)。
当找到大小足够的内存块之后:
- 若将整个内存分配给pcb,则将内存块从freeblocks中删除,同时将该内存块直接放到usedblocks中,同时设置该内存块的pid和pcb的blockID字段。
- 若将内存分成两块:首先将内存块分成left和right两个内存块,划分内存块的时候需要注意是将left划分给pcb还是将right划分给pcb,分配给pcb的内存块需要设置pid字段,pcb也需要设置blockID字段。将分配出去的内存块放入到usedblocks中去,未分配出去的则放入到freeblocks中,同时还需要记得删除原始内存块。
- 若将内存分成三块:将内存块按照left,middle,right三块划分,这是middle的pid字段以及pcb的blockID字段。将left和right插入到freeblocks中,将middle插入到usedblocks中,同时需要在freeblocks中删除原来的内存块。
循环首次适应算法
思路:每一次查找空闲分区块不再是从头开始找了,而是从上次找到空闲分区块的下一个空闲分区开始查找,直到能找到一个满足要求的空闲分区,然后从中划分出一个大小相等的内存空间即可。分配内存的三种过程和首次适应算法都是一样的。和首次适应算法不一样的如下:
本算法使用迭代器fit来指示下一次起始查询的空闲分区块,若当前分区块已经是链尾则将迭代器指向起始位置。查找空闲分区块使用另一个迭代器it。
每一次循环给pcb分配内存的时候都将it置为fit,从fit位置开始查找,若该位置满足要求则直接开始分配,若不满足则循环查找,循环一圈再一次到fit还没找到的时候,该内存则分配失败。
在每一次找到大小满足的内存之后,都需要先记录下it迭代器下一个空闲分区块的needid,将内存分配给pcb之后,将it迭代器对应位置的内存分区块删除之后,插入新的内存块(这些步骤在首次适应算法中已经给出)。然后根据之前记录的needid更新fit迭代器。
回收算法
回收内存块的最终目的是将之前分配出去的各种内存块最后整个成起始内存块(起始地址为0,大小为1024),所以我们进行循环回收,直到usedblocks中的内存块为0并且freeblocks中的内存块数量为1的时候才回收结束。在回收的时候存在两种情况:
usedblocks不为空的时候:每一次取出usedblocks中的第一个内存块ublock,然后循环遍历freeblocks块,查找是否满足ublock的起始地址加上ublock的大小刚好等于遍历过程中freeblocks元素的起始地址,或者freeblocks元素的起始地址加上它的大小等于ublock的起始地址,若存在则合并这两个内存块。不存在则将ublock内存块按照起始地址的大小插入到freeblocks中。
usedblocks为空时:每一次取出freeblocks第一个元素和第二个元素进行合并。直到合并为起始内存块时结束。
代码
代码分为三个代码:dpma.hpp,one.cc,two.cc。其中one.cc调用的是首次适应算法,two.cc调用的是循环首次适应算法。dpma.hpp则是实现了所有算法。
dpma.hpp
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <algorithm>
struct Block {
int _id; //分区的序号
int _size; //分区的大小
int _startAddr; //分区的开始位置
bool _status; //分区的状态:true为空闲,false为被占用
int _pid; //如果该分区被占用,则存放占用进程的id; 否则为-1
Block(int id, int startAddr, int size)
: _id(id)
, _startAddr(startAddr)
, _size(size)
, _status(true)
, _pid(-1)
{
}
Block(int id, int startAddr, int size, int pid)
: _id(id)
, _startAddr(startAddr)
, _size(size)
, _status(true)
, _pid(pid)
{
}
};
struct PCB {
int _pid; //进程的序号
int _neededMem; //需要的内存分区大小
int _status; //1: 内存分配成功;-1:分配失败;0:还未分配
int _blockID; //如果分配成功,保存占用分区的id,否则为-1
PCB(int pid, int neededMem)
: _pid(pid)
, _neededMem(neededMem)
, _status(0)
, _blockID(0)
{
}
};
#define MEOMERYSIZE 1024
#define PROCESSNUM 10
class DPMAllocation {
private:
void GenerateProcess() {
for (int i = 0; i < PROCESSNUM; i++) {
int neededMem = rand() % 101 + 100;
_pcbs.emplace_back(i, neededMem);
}
printf(" ProcID neededMem\n");
for (auto& pcb : _pcbs)
printf(" %5d %9d\n", pcb._pid, pcb._neededMem);
std::cout << "****************************************************" << std::endl;
_free_blocks.emplace_back(_blockID, 0, MEOMERYSIZE);
printf("FreeBlkID StartAddr Size\n");
printf("%9d %9d %4d\n", 0, 0, MEOMERYSIZE);
std::cout << "****************************************************" << std::endl;
}
void ShowFreeBlk_UsedBlk() {
printf("FreeBlkID StartAddr Size\n");
for (auto& block : _free_blocks)
printf("%9d %9d %4d\n", block._id, block._startAddr, block._size);
if (!_used_blocks.empty()) {
std::cout << "\n";
printf("UsedBlkID StartAddr Size ProcID\n");
for (auto& block : _used_blocks)
printf("%9d %9d %4d %6d\n", block._id, block._startAddr, block._size, block._pid);
}
std::cout << "****************************************************" << std::endl;
}
void MeomeryAllocationFirstFit() {
// 分别取出一个进程
for (auto& pcb : _pcbs) {
// 在freeblock中查找是否存在合适大小的空间
printf("Allocating memory for process %d, memory requirement = %d\n", pcb._pid, pcb._neededMem);
auto it = _free_blocks.begin();
for (; it != _free_blocks.end(); ++it)
if (it->_size >= pcb._neededMem)
break;
if (it == _free_blocks.end()) {
printf("Memory allocation failed for process %d !\n", pcb._pid);
pcb._status = -1;
ShowFreeBlk_UsedBlk();
continue;
}
pcb._status = 1;
// 当前it指向的内存块有足够的空间,开始随机化分配
// 一共存在三种情况。分成三个区间,分成两个区间,刚好一个区间
// 当it->size == neededMem的时候出现摸零的情况
int addr;
if (it->_size != pcb._neededMem)
addr = rand() % (it->_size - pcb._neededMem) + it->_startAddr;
// 刚好一个区间
if (it->_size == pcb._neededMem) {
Block total(it->_id, it->_startAddr, it->_size, pcb._pid);
_free_blocks.erase(it);
_used_blocks.emplace_back(total);
pcb._blockID = total._id;
ShowFreeBlk_UsedBlk();
continue;
}
else if (addr == it->_startAddr || addr + pcb._neededMem == it->_size) {
// 分成了左右两个区间[addr, addr + it->size][]
// 将左边分配出去
if (addr == it->_startAddr) {
Block left(it->_id, addr, pcb._neededMem, pcb._pid);
Block right(_blockID + 1, it->_startAddr + pcb._neededMem, it->_size - pcb._neededMem);
_free_blocks.insert(it, right);
_free_blocks.erase(it);
_used_blocks.emplace_back(left);
pcb._blockID = left._id;
}
else {
// 将右边分配出去
Block left(it->_id, it->_startAddr, it->_size - pcb._neededMem);
Block right(_blockID + 1, addr, pcb._neededMem, pcb._pid);
_free_blocks.insert(it, left);
_free_blocks.erase(it);
_used_blocks.emplace_back(right);
pcb._blockID = right._id;
}
_blockID++;
ShowFreeBlk_UsedBlk();
continue;
}
// [it->_startAddr addr - it->_startAddr][addr neededMem][addr+neededMem it->_startAddr + it->_size - addr - pcb._neededMem]
Block left(it->_id, it->_startAddr, addr - it->_startAddr);
Block right(_blockID + 1, addr + pcb._neededMem, it->_startAddr + it->_size - addr - pcb._neededMem);
Block middle(_blockID + 2, addr, pcb._neededMem, pcb._pid);
pcb._blockID = middle._id;
_blockID += 2;
_used_blocks.emplace_back(middle);
// 现在将left和right插入到应该插入的地方
_free_blocks.insert(it, {
left, right });
_free_blocks.erase(it);
ShowFreeBlk_UsedBlk();
}
}
void CombineUsed_Free() {
auto ublock = _used_blocks.front();
printf("Recycling used memory block for process %d\n", ublock._pid);
auto fit = _free_blocks.begin();
for (; fit != _free_blocks.end(); ++fit) {
if (fit->_startAddr + fit->_size == ublock._startAddr
|| ublock._startAddr + ublock._size == fit->_startAddr)
break;
}
if (fit == _free_blocks.end()) {
// 将其插入到freeblock中
fit = _free_blocks.begin();
for (; fit != _free_blocks.end(); ++fit) {
if (fit->_startAddr >= ublock._startAddr + ublock._size)
break;
}
_free_blocks.insert(fit, ublock);
} else {
if (fit->_startAddr + fit->_size == ublock._startAddr)
fit->_size += ublock._size;
else<

最低0.47元/天 解锁文章
8615

被折叠的 条评论
为什么被折叠?



