提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
所有类都在一个包(os.test2)里,步骤4里的函数都封装在Distribute类里。
一、实验原理
动态分区分配是根据进程的实际需要,动态地为作业从可用内存中划分出一个分区,刚好能满足作业的实际需要。而在分配时,须按照一定的分配算法,从空闲分区表或空闲分区链中选出一分区分配给该作业。
在本实验中运用了四种分配算法,分别是首次适应算法,循环首次适应算法,最坏适应算法,最佳适应算法。
实验要求:
二、实验步骤
1.创建PCB类和MemoryBlock类
代码如下:
PCB:进程
package os.test2;
public class PCB {
String name; //进程名
int size; //进程大小
int sadd; //进程分配起始地址
int areaId; //进程分配所在内存块区号
public PCB(String name,int size){
this.name=name;
this.size=size;
sadd=-1; //-1表示未分配
areaId=-1; //-1表示未分配
}
}
MemoryBlock:进程块
package os.test2;
public class MemoryBlock {
int size; //大小
int sadd; //起始地址
int area_code; //区号
public MemoryBlock(int size,int sadd,int area_code){
this.size=size;
this.sadd=sadd;
this.area_code=area_code;
}
}
2.创建FreeTable类
空闲分区表
代码如下:
package os.test2;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class FreeTable {
List<MemoryBlock> freeTable =new ArrayList(); //初始化的空闲分区表
public boolean check(int size,int sadd){ //检测是否可以添加大小为size,起始地址为sadd的内存块
if(freeTable.isEmpty()) //情况1:空闲分区表中无内存块
return true;
for(MemoryBlock cur:freeTable){ //情况2:空闲分区表中有内存块
if(sadd==cur.sadd) //情况2.1:空闲分区表中有输入的内存块起始地址与某个内存块重复
return false;
if(sadd<cur.sadd){
if(sadd+size<=cur.sadd);
else
return false; //情况2.2:空闲分区表中有输入内存块与某个高地址内存块空间重合
}
else{
if(sadd>=cur.sadd+cur.size); //情况2.2:空闲分区表中有输入内存块与某个低地址内存块空间重合
return false;
}
}
return true;
}
public void addMeb() { //初始化添加内存块
Scanner sc = new Scanner(System.in);
do {
System.out.println("请输入大小、始址:");
int size = sc.nextInt();
while(size<=0) {
System.out.println("大小不合法,请重新输入!");
size = sc.nextInt();
}
int sadd = sc.nextInt();
while(sadd<0) {
System.out.println("地址不合法,请重新输入!");
sadd= sc.nextInt();
}
if (check(size,sadd)) { //check()函数检测为true才进空闲分区表
MemoryBlock cur=new MemoryBlock(size,sadd,freeTable.size());
freeTable.add(cur);
System.out.println("内存块添加成功!是否继续添加:1:是 2:否 ");
int flag=sc.nextInt();
if(flag==1);
else
break;
}
}while(true);
}
public void findMebTable(List<MemoryBlock>MBList){ //查看分区表
if(MBList.isEmpty())
System.out.println("当前无内存块!");
for(MemoryBlock cur:MBList)
System.out.println("区号:"+cur.area_code+" 大小:"+cur.size+" 起始地址:"+cur.sadd);
}
}
3.设计主窗口类
主要实现初始化空闲分区表和进程添加的界面。
代码如下:
package os.test2;
import java.util.*;
public class Psch {
static FreeTable ft=new FreeTable(); //初始化空闲分区表
static List<PCB>pcbList=new ArrayList<>(); //初始化进程序列
static HashSet<String>set=new HashSet<>(); //set添加进程名防止重复添加
//添加进程
public static void addPCB(){
Scanner sc=new Scanner(System.in);
int count=1;
do {
System.out.println("请输入进程名:");
String name = sc.next();
while (set.contains(name)) {
System.out.println("已有此进程,请重新输入进程名:");
name = sc.next();
}
set.add(name);
System.out.println("请输入进程大小:");
int size = sc.nextInt();
pcbList.add(new PCB(name, size));
System.out.println("是否继续添加:1:是 2:否");
count=sc.nextInt();
}while(count==1);
}
//打印进程表
public static void findPCBS(List<PCB> pcbs){
for(PCB pcb:pcbs){
System.out.print("进程名:"+pcb.name+" 进程大小:"+pcb.size);
if(pcb.sadd==-1)
System.out.println(" 未分配内存");
else{
System.out.print(" 已分配内存");
System.out.println(" 起始地址:"+pcb.sadd+" 分配内存区号:"+pcb.areaId);
}
}
}
//初始化界面函数
public static void MainMenu() {
Scanner sc=new Scanner(System.in);
int count1 = 1;
System.out.println("初始化内存块:");
while (count1 == 1) {
System.out.println("**************************");
System.out.println("请输入你需要的服务:");
System.out.println("1.添加空闲内存块");
System.out.println("2.查看空闲分区表");
System.out.println("3.内存分配");
System.out.println("0.退出");
int num = sc.nextInt();
switch (num) {
case 1:
ft.addMeb();
break;
case 2:
ft.findMebTable(ft.freeTable);
break;
case 3:
distribute();
break;
case 0:
count1 = 0;
break;
default:
System.out.println("输入非法,请输入其他选项!");
break;
}
}
}
//分配界面函数
public static void distribute() {
Distribute dis = new Distribute();
Scanner sc = new Scanner(System.in);
int count=1;
while (count == 1) {
System.out.println("**************************");
System.out.println("请输入你需要的服务:");
System.out.println("1.添加进程");
System.out.println("2.查看进程");
System.out.println("3.进程分配");
System.out.println("0.退出");
int num = sc.nextInt();
switch (num) {
case 1:
addPCB();
break;
case 2:
findPCBS(pcbList);
break;
case 3:
dis.disMB();
break;
case 0:
count=0;
break;
default:
System.out.println("输入非法,请输入其他选项!");
break;
}
}
}
public static void main (String[]args){
MainMenu();
}
}
4.进程分配与回收类(Distribute类)
主要实现四种分配算法,分别是首次适应算法,循环首次适应算法,最坏适应算法,最佳适应算法,以及分配后的回收算法。
1.分配进程界面函数
代码如下:
public void disMB() { //为进程分配空间,算法选择界面
Scanner sc = new Scanner(System.in);
int count = 1;
while (count == 1) {
List<MemoryBlock> useMB = new ArrayList<>();
for (MemoryBlock cur : ft.freeTable)
useMB.add(new MemoryBlock(cur.size, cur.sadd,cur.area_code)); //复制内存块用例
List<PCB> usePCB = new ArrayList<>();
for (PCB cur : pcbList)
usePCB.add(new PCB(cur.name, cur.size)); //复制进程用例
System.out.println("**************************");
System.out.println("请选择进程分配算法:");
System.out.println("1.最佳适应算法");
System.out.println("2.最坏适应算法");
System.out.println("3.首次适应算法");
System.out.println("4.循环首次适应算法");
System.out.println("0.退出");
System.out.println("**************************");
int num = sc.nextInt();
switch (num) {
case 1 -> this.BF(usePCB, useMB);
case 2 -> this.WF(usePCB, useMB);
case 3 -> this.FF(usePCB, useMB);
case 4 -> this.NF(usePCB, useMB);
case 0 -> count = 0;
default -> System.out.println("输入非法,请输入其他选项!");
}
}
}
2.最佳适应算法(BF)
代码如下:
//最佳适应算法
public void BF(List<PCB>usePCB,List<MemoryBlock>useMB) {
useMB.sort(Comparator.comparingInt(x -> x.size)); //内存块以大小升序排列
for (PCB pcb : usePCB) {
for(MemoryBlock cur:useMB){
if(useMB.isEmpty())
break;
if(pcb.size>cur.size);
//内存块符合分配的两种情况:内存完全分配出去和部分分配出去
else{
int index=useMB.indexOf(cur);
if(cur.size==pcb.size){ //内存块完全分配出去
pcb.areaId=cur.area_code;
pcb.sadd=cur.sadd;
useMB.remove(index);
break;
}
pcb.areaId=cur.area_code; //内存块部分分配出去了->剩余所有空闲内存块重新排序
pcb.sadd=cur.sadd;
cur.sadd+= pcb.size;
cur.size-= pcb.size;
useMB.sort(Comparator.comparingInt(x -> x.size));
break;
}
}
}
this.dised(usePCB,useMB);
}
3.最坏适应算法(WF)
代码如下:
//最坏适应算法
public void WF(List<PCB>usePCB,List<MemoryBlock>useMB){
useMB.sort((x1,x2)-> x2.size-x1.size); //内存块以大小降序排列
for (PCB pcb : usePCB) {
if(useMB.isEmpty())
break;
MemoryBlock cur=useMB.get(0); //只需看最大的进程是否满足
if(pcb.size<=cur.size){ //满足:内存块符合分配的两种情况:内存完全分配出去和部分分配出去
int index=useMB.indexOf(cur);
if(cur.size==pcb.size){ //内存块完全分配出去
pcb.areaId=cur.area_code;
pcb.sadd=cur.sadd;
useMB.remove(index);
continue;
}
pcb.areaId=cur.area_code; //内存块部分分配出去了->剩余所有空闲内存块重新排序
pcb.sadd=cur.sadd;
cur.sadd+= pcb.size;
cur.size-= pcb.size;
useMB.sort((x1, x2) -> x2.size - x1.size);
}
}
this.dised(usePCB,useMB);
}
4.首次适应算法(FF)
代码如下:
//首次适应算法
public void FF(List<PCB>usePCB,List<MemoryBlock>useMB){
useMB.sort(Comparator.comparingInt(x -> x.sadd));
for (PCB pcb : usePCB) {
for(MemoryBlock cur:useMB){
if(useMB.isEmpty())
break;
if(pcb.size>cur.size) ;
else{ //内存块符合分配的两种情况:内存完全分配出去和部分分配出去
int index=useMB.indexOf(cur);
if(cur.size==pcb.size){ //内存块完全分配出去
pcb.areaId=cur.area_code;
pcb.sadd=cur.sadd;
useMB.remove(index);
break;
}
pcb.areaId=cur.area_code; //内存块部分分配出去了->剩余所有空闲内存块重新排序
pcb.sadd=cur.sadd;
cur.sadd+= pcb.size;
cur.size-= pcb.size;
break;
}
}
}
this.dised(usePCB,useMB);
}
5.循环首次适应算法(NF)
代码如下:
//循环首次适应算法
public void NF(List<PCB>usePCB,List<MemoryBlock>useMB) {
useMB.sort(Comparator.comparingInt(x -> x.sadd));
MemoryBlock cur = useMB.get(0);
for (PCB pcb : usePCB) {
if (useMB.isEmpty())
break;
int curIndex = useMB.indexOf(cur);
while (pcb.size > cur.size) { //cur不满足
int index = useMB.indexOf(cur);
if (useMB.size() > index + 1) //分区表后面还有内存块
cur = useMB.get(index + 1);
else // 分区表后面没有内存块,即cur为空闲分区表最后一个,则从头开始再去适配
cur = useMB.get(0);
if (index == curIndex) //已经找了一圈(全部的空闲内存块)还是没有符合的
break;
}
if (pcb.size > cur.size) ; //即上面index==curIndex(已经找了一圈还是没有符合的)这种情况,放弃为pcb分配内存空间
else { //满足:内存块符合分配的两种情况:内存完全分配出去和部分分配出去
int index = useMB.indexOf(cur);
if (cur.size == pcb.size) { //内存块完全分配出去
pcb.areaId=cur.area_code;
pcb.sadd=cur.sadd;
useMB.remove(index);
cur=useMB.size() > index ?useMB.get(index):useMB.get(0); //cur指向进程表的下一个(无下一个就是指向开头)
continue;
}
pcb.areaId=cur.area_code;
pcb.sadd=cur.sadd;
cur.sadd += pcb.size;
cur.size -= pcb.size;
}
}
this.dised(usePCB, useMB);
}
6.分配后功能界面
每次调用分配算法之后,可以查看进程分配情况和空闲分区表使用情况,也可以选择回收进程。
代码如下:
//分配后功能界面
public void dised(List<PCB> usePCB, List<MemoryBlock> useMB) { //已经分配成功,输出或者回收
Scanner sc = new Scanner(System.in);
int count2 = 1;
do {
System.out.println("**************************");
System.out.println("请输入你需要的服务:");
System.out.println("1.查看进程分配情况");
System.out.println("2.查看空闲分区表");
System.out.println("3.内存回收");
System.out.println("0.退出");
int num = sc.nextInt();
switch (num) {
case 1 -> findPCBS(usePCB);
case 2 -> ft.findMebTable(useMB);
case 3 -> this.retrieve(usePCB, useMB);
case 0 -> count2 = 0;
default -> System.out.println("输入非法,请输入其他选项!");
}
} while (count2 == 1);
}
7.回收进程算法
代码如下:
//回收算法
public void retrieve(List<PCB> usePCB, List<MemoryBlock> useMB) { //回收函数
HashMap<String, PCB> pcbMap = new HashMap<>(); //已经分配成功的进程,建立进程名与进程的关系
Scanner sc = new Scanner(System.in);
for (PCB pcb : usePCB) {
if (pcb.sadd == -1) ;
else
pcbMap.put(pcb.name, pcb);
}
int flag = 1;
while (flag == 1) {
if (pcbMap.isEmpty()) { //basecase
System.out.println("无可回收进程!");
break;
}
System.out.println("已分配内存进程如下:");
for (PCB pcb : pcbMap.values())
System.out.println("进程名:" + pcb.name + " 进程大小:" + pcb.size + " 起始地址:" + pcb.sadd+" 所在区号:"+pcb.areaId);
System.out.println("请选择要回收的进程:");
String name = sc.next();
while (!pcbMap.containsKey(name)) {
System.out.println("此进程名输入无效,请重新输入!");
name = sc.next();
}
PCB rem = pcbMap.remove(name);
MemoryBlock curt=null;
for (MemoryBlock cur : useMB) { //3种情况
curt=cur;
if (rem.areaId != cur.area_code);
else {
if(cur.sadd== rem.sadd+rem.size){
cur.sadd = rem.sadd;
cur.size += rem.size;
rem.sadd = -1;
return;
}else{
MemoryBlock mb=new MemoryBlock(rem.size, rem.sadd,rem.areaId);
useMB.add(mb);
rem.sadd=-1;
rem.areaId=-1;
return;
}
}
}
if (rem.areaId != curt.area_code){
MemoryBlock mb=new MemoryBlock(rem.size, rem.sadd,rem.areaId);
useMB.add(mb);
rem.sadd=-1;
rem.areaId=-1;
}
System.out.println("是否继续回收:1:是 2:否");
flag = sc.nextInt() == 1 ? 1 : 2; //非1都可退出
}
}
总结
回收算法还有些瑕疵,以后再改。