设计程序模拟内存的动态分区内存管理方法。内存空闲区使用空闲分区表进行管理,采用最先适应算法从空闲分区表中寻找空闲区进行分配,内存回收时不考虑与相邻空闲区的合并。
假定系统的内存共640K,初始状态为操作系统本身占用40K。
t1 时刻,为作业A、B、C分配80K、60K、100K、的内存空间;
t2 时刻作业B完成;
t3 时刻为作业D分配50K的内存空间;
t4 时刻作业C、A完成;
t5 时刻作业D完成。
要求编程序分别输出t1、t2、t3、t4、t5时刻内存的空闲区的状态。
数据结构定义:
//进程PCB类型的描述
struct PCB
{
char name; //进程名
int address; //进程分区起止
int len; //进程所占分区长度
};
struct PCB PCBelem[maxPCB];//进程占用内存表
//分区类型的描述
struct Part
{
int address; //空闲分区起始地址
int len; //空闲分区大小
};
struct Part Partelem[maxPart]; //空闲分区表
主要变量说明:
int length = 640 ; //系统有 640 KB 的空闲
int fnum = 0; //记录总的分区数量
int jnum = 0; //记录总的进程数量
int leng = 0; //临时变量
函数说明:
void init4IOS(int tem) //为操作系统分配40k内存
int getTagByPcb(char name) //判定输入的进程是否存在以及位置
void request() //进程分配请求
void getPrint() //打印空闲分区
void jcPrintf() //打印进程
void release() //回收指定进程内存
关键点叙述:
(1) 内存结构的建立及表示
分别建立进程占用内存表和空闲分区表,由于进程和空闲分区的所有属性值不一样,所以需要分别建表,它们之间起始地址是连续的,逻辑上形成一整块内存。
(2) 进程请求分配
当进程提出内存分配请求以后,首先需要做的是在空闲分区表当中寻找一个单独的,足够请求进程分配的内存空间。注意,寻求该空间需要从内存低址开始寻找,这样有利于保护高址大块内存,当然,缺点就是会产生较多的碎片内存,这些内存难以被利用。(本程序未涉及相邻空闲空间的合并,以及内存空间的紧凑)若存在这样一个空间,则从该空间当中划出请求进程所需的内存大小,将该内存块(含有起始地址等信息的结构体结点)存入进程占用内存表当中,同时,对于被划分的内存空间,需要修改其起始地址,达到逻辑上的合理。值得一提的是,若内存分配以后,被划分内存空间大小为0,则需要除去该条记录(移除(覆盖)空闲分区表)。
(3)回收进程
当对指定进程提出回收要求时,会产生两个反应,一是会对空闲分区表插入一块内存,用以表示被回收内存的空闲已空闲出来;二是对于被回收的进程,不应该出现在进程占用表当中,所以应当将其移除进程占用表。
对于以上两个操作的实现:对空闲分区表插入空闲内存时,需要从高址内存空间开始对比插入内存空间的大小,当出现内存空间的起始地址小于插入内存空间的起始地址时停止对比查找,将其后的内存空间均向后移动一位,以腾出一个位置用于插入需要插入的空闲内存空间,这样在物理结构上也就合理了。除去被回收的进程,思想与上类似,找到该进程以后,将该进程以后的结点均向前移动一位,末尾指向相应减一,除去被回收的进程目的在于避免对同一进程进行重复回收。
存在问题:
模拟内存管理所实现效果简单,与真实内存分配存在很大差异。
改进:
(1) 实现相邻空闲空间的合并
(2) 内存空间紧凑
(3) 其他优化
个人总结:
1、 本程序编写完成以后,初步理解内存管理过程。程序实现的功能较为简单,没有考虑相邻空闲分区的合并,以及碎片空间的紧凑整理等操作。
2、越努力,越幸运!_
完整代码:
#include "stdio.h"
#include "Windows.h"
#define maxPCB 100 //定义最大PCB结点数
#define maxPart 100 //定义最大分区
//进程PCB类型的描述
struct PCB
{
char name; //进程名
int address; //进程分区起止
int len; //进程所占分区长度
};
struct PCB PCBelem[maxPCB];//进程占用内存表
//分区类型的描述
struct Part
{
int address; //空闲分区起始地址
int len; //空闲分区大小
};
struct Part Partelem[maxPart];//空闲分区表
int length = 640 ; //系统有 640 KB 的空闲
int fnum = 0; //记录总的分区数量
int jnum = 0; //记录总的进程数量
struct Part part; //公共使用临时结点(分区)
struct PCB pcb; //公共使用临时结点(进程)
//为操作系统分配40k内存
void init4IOS(int tem)
{
length = length - tem; //剩余系统空闲空间减少
part.address = 0 + 40; //操作系统占用,空闲内存从40开始
part.len = length; //空闲内存大小
Partelem[fnum] = part; //存入空闲分区表
fnum ++; //分区数增加
}
//判定输入的进程是否存在以及位置
int getTagByPcb(char name)
{
int i;
for(i = 0; i < jnum; i ++)
{
if(name == PCBelem[i].name)
{
return i;
}
}
printf("\n\t\t找不到进程名%c,请重新输入!\n",name);
return -1;
}
//进程分配请求
void request()
{
char c = 0;
while(true)
{
printf("\n\t\t请输入请求内存进程 名称:");
fflush(stdin);//清空缓冲区
scanf("%c",&pcb.name);
//检查是否已存在进程
for(int j = 0; j < jnum; j++)
{
if(PCBelem[j].name == pcb.name)
{
printf("\n\t\t进程 %c 已存在,可尝试输入其他名称,也可以先回收该进程!\n",pcb.name);
return;
}
}
printf("\n\t\t\t\t 长度:");
fflush(stdin);//清空缓冲区
scanf("%d",&pcb.len);
length = length - pcb.len; //减去相对应的操作系统剩余空闲空间
if(length <= 0)
{
if(length == 0)
{
printf("\n\t\t警告:系统资源已经全部分配!\n");
}
else
{
length = length + pcb.len; //分配失败将内存换回去,以免溢出
printf("\n\t\t未找到合适空间或者系统资源不足!\n"); return;
}
}
//如果符合分配条件,进行分配
for(int i = 0; i < fnum; i++)
{
//寻找一个可以分配的空间
if(pcb.len <= Partelem[i].len)
{
//改变进程占用地址
pcb.address = Partelem[i].address;
//保存该进程
PCBelem[jnum++] = pcb;
//对空闲分区进行划分
Partelem[i].address = Partelem[i].address + pcb.len;
Partelem[i].len = Partelem[i].len - pcb.len;
break;//关键作用(从低址找到一个空间就可以了,没必要再往后找了)
}
}
//除去分配后空闲空间为0的记录
if(Partelem[i].len == 0)
{
int leng = i;
//进行前移覆盖
while(leng != fnum)
{
part.address = Partelem[leng+1].address;
part.len = Partelem[leng+1].len;
Partelem[leng] = part;
leng++;
}
//分区数减少
fnum--;
}
printf("\n\t\t是否要继续输入进程?(Y/y) 是/(N/n) 否:");
fflush(stdin);
c = getchar();
fflush(stdin);
if(c=='N'||c=='n')
{
break;
}
}
}
//打印空闲分区
void getPrint()
{
printf("\t\t----------------------空闲分区 begin----------------------\n");
int j = 1;
for (int i = 0;i < fnum; i ++)
{
printf("\n\t\t第%d块空闲内存 起始地址为%d,容量为%d\n",j,Partelem[i].address,Partelem[i].len);
j ++;
}
printf("\n\t\t----------------------空闲分区 end ----------------------\n");
}
//打印进程
void jcPrintf()
{
printf("\n\t\t名称\t起始地址\t大小\n");
for(int i = 0 ; i < jnum; i++)
{
printf("\n\t\t%2c\t%4d\t\t%d KB\n",PCBelem[i].name,PCBelem[i].address,PCBelem[i].len);
}
}
//回收指定进程内存
void release()
{
int i = 0;
char name;
printf("\n\t\t请输入想要回收的进程名称:");
fflush(stdin);//清空缓冲区
scanf("%c",&name);
if(getTagByPcb(name) == -1)
{
printf("\n\t\t该进程不存在或者已经被回收!\n");
return;
}
printf("\n\t\t正在回收%c的内存:",name);
for(int j = 0; j < 15; j++)
{
printf("▊");
Sleep(200);
}
printf(" 完成 \n");
//for循环寻找该进程
for(i = fnum; i >= 0; i --)
{
int leng = fnum;
if(PCBelem[getTagByPcb(name)].address > Partelem[i-1].address || i == 0)
{
//while循环为该进程腾出一个位置
while(leng != i)
{
part.address = Partelem[leng-1].address;
part.len = Partelem[leng-1].len;
Partelem[leng] = part;
leng--;
}
break;//关键(从高址往前找到一个空间就可以了,没必要再往前找了)
}
}
//系统空闲空间对应增加
length = length + PCBelem[getTagByPcb(name)].len;
//使用公共的结点记录即将产生的空闲空间
part.address = PCBelem[getTagByPcb(name)].address;
part.len = PCBelem[getTagByPcb(name)].len;
//将该结点存入之前腾出的位置
Partelem[i] = part;
//分区数增加
fnum ++;
//对进程占用内存表进行调整,除去被回收进程
int leng = getTagByPcb(name);
//进行前移覆盖
while(leng != jnum)
{
pcb.name = PCBelem[leng+1].name;
pcb.address = PCBelem[leng+1].address;
pcb.len = PCBelem[leng+1].len;
PCBelem[leng] = pcb;
leng++;
}
//进程数减少
jnum--;
}
void main()
{
char tem = 0;
int OSsize = 40;
int b = 1, k;
//为操作系统分配内存
init4IOS(OSsize);
while (b)
{
system("cls");
printf("\n\n\t\t操作系统内存分配\n\n");
printf("\t\t已为操作系统分配了 40 KB 内存\n",tem);
printf("\n\t\t ----------------------------\n");
printf("\t\t|1.... 请求分配内存 |\n");
printf("\t\t|2.... 输出空闲分区 |\n");
printf("\t\t|3.... 强制进程结束 |\n");
printf("\t\t|4.... 输出进程信息 |\n");
printf("\t\t|0.... 退出 |\n");
printf("\t\t ----------------------------\n\n");
printf("\t\t当前操作系统空闲内存:%d KB\n",length);
printf("\n\t\t请选择:");
fflush(stdin);//清空缓冲区
scanf("%d", &k);
switch (k)
{
case 1: request(); break;
case 2: getPrint(); break;
case 3: release(); break;
case 4: jcPrintf(); break;
case 0: b = 0; break;
default:printf("\n\t\t输入无效!\n");break;
}
if (b != 0) { printf("\n\t\t"); system("pause"); }
}
}
如有错误,欢迎指正!