前言:虽说操作系统的课程已经结束,但是我觉得梁老师的课上有很多值得记录和回味的东西,时隔多日,我决定还是来写一些博客,记录我这门课上写的实践作业。
题目:用C语言(也可以用Java)实现采用首次适应算法的内存分配和回收过程。
题目要求:
分析:首先,我们需要三个类,分别为内存、小内存块(分区)和PCB(用来记录进程信息),至于适应算法的实现,我们可以在内存类的内部实现,也可以用外部方法实现。
如果大家对四个适应算法不熟悉的话可以看看书或者查查资料,在这里我就不进行过多的描述。
PS.这里我就写出我的核心代码,部分例如toString()方法的代码省略
1.小内存块Hole(可以表示内存中的各个分区,也可以表示进程对应的内存块)
public class Hole {
private int head; //小内存块的起始地址
private int size; //小内存块的大小
private boolean isFree; //小内存块的空闲状态
public Hole(int head, int size) {
this.head = head;
this.size = size;
this.isFree = true;
}
}
2.PCB(记录进程信息和进程对应的内存块)
public class Pcb {
private int id; //进程id
private int state; //进程状态 0为空闲 1为就绪 2为执行 3为阻塞
private Hole hole; //进程所对应的小内存块
public Pcb(int id, int state, Hole hole) {
this.id = id;
this.state = state;
this.hole = hole;
}
}
3.内存
public class Memory {
private int size; //内存块大小
private int lastFind; //上次寻址结束的下标
private LinkedList pcbs; //记录内存块中进程的双向链表
private LinkedList holes; //记录内存块分区的双向链表
private static final int MIN_SIZE = 5 //最小剩余分区大小
}
1.构造函数
public Memory(int size) {
this.size = size; //初始化内存大小
this.pcbs = new LinkedList<>(); //初始化两个链表
this.holes = new LinkedList<>();
holes.add(new Hole(0, size)); //分区链表的首项为大小是内存大小的空闲分区
}
2.申请内存
由于我是用外部方法实现对分区的计算,所以在内部类中只是单纯的分配Hole。
public Memory getMemory(int size, int location, Hole hole) { //size为申请大小 location为分配分区的下标 hole为location对应的分区
if (hole.getSize() - size >= MIN_SIZE) { //若分配后当前分区剩余大小大于最小分区大小,则把当前分区分为两块
Hole newHole = new Hole(hole.getHead() + size, hole.getSize() - size);
holes.add(location + 1, newHole);
hole.setSize(size);
}
pcbs.add(new Pcb(10000 + (int)(89999 * Math.random()), 1, hole)); //模拟添加一个就绪状态的进程,此进程id随机生成(忽略id重复的情况哈)
hole.setFree(false); //设置当前分区为非空闲状态
System.out.println("成功分配大小为" + size + "的内存");
return this;
}
3.释放内存(通过分区id或者模拟的进程id释放,此方法在类Memory中)
释放时有四种情况,分别是释放分区前的分区为空闲分区、释放分区后的分区为空闲分区、释放分区前后的分区都为空闲分区和释放分区前后的分区都不为空闲分区,在代码中我都会解释。
public Memory releaseMemory(int id) {
Pcb pcb = null; //记录此id对应进程(忽略进程id与分区id相同,但进程不同的情况哈)
if (id >= holes.size()) { //若id大于holes的表长度,则需要判断此id是否是进程id
boolean flag = false;
for (int i = 0; i < pcbs.size(); i++) { //循环比对此id是否是pcbs链表中进程的id
if (pcbs.get(i).getId() == id) {
pcb = pcbs.get(i);
flag = true;
break;
}
}
if (!flag) {
System.err.println("无此分区:" + id);
return this;
}
}
if (pcb != null) { //若是通过进程id释放内存,则用下列循环获取进程对应的hole对应holes链表的下标(获取分区id)
for (int i = 0; i < holes.size(); i++) {
Hole hole = holes.get(i);
if ((pcb.getHole().getSize() == hole.getSize()) && (pcb.getHole().getHead() == hole.getHead())) {
id = i;
break;
}
}
}
Hole hole = holes.get(id); //此id为分区id
if (hole.isFree()) {
System.out.println("此分区空闲,无需释放:\t" + id);
return this;
}
//用分区id释放pcb
for (int i = 0; i < pcbs.size(); i++) {
Pcb pcb2 = pcbs.get(i);
if ((pcb2.getHole().getSize() == hole.getSize()) && (pcb2.getHole().getHead() == hole.getHead())) {
pcbs.remove(i);
break;
}
}
//如果回收分区不是尾分区且后一个分区为空闲, 则与后一个分区合并
if (id < holes.size() - 1 && holes.get(id + 1).isFree()) {
Hole nextHole = holes.get(id + 1);
hole.setSize(hole.getSize() + nextHole.getSize());
holes.remove(nextHole);
}
//如果回收分区不是首分区且前一个分区为空闲, 则与前一个分区合并
if (id > 0 && holes.get(id - 1).isFree()) {
Hole lastHole = holes.get(id - 1);
lastHole.setSize(hole.getSize() + lastHole.getSize());
holes.remove(id);
id--;
}
holes.get(id).setFree(true);
System.out.println("内存回收成功!");
return this;
}
这里用两个判断就完成了对四种情况的处理,大家仔细看看哈。
1.首次适应算法
public Memory 首次适应算法(Memory memory, int size) {
int sum = 0;
//循环内存中所有分区
for (int i = 0; i < memory.getHoles().size(); i++) {
sum++;
//为循环首次适应算法设置最后寻址的下标
memory.setLastFind(i);
Hole hole = memory.getHoles().get(i); //获得对应的分区
//若此分区空闲且大小大于申请的大小,则申请内存
if (hole.isFree() && hole.getSize() >= size) {
System.out.println("查找" + sum + "次");
return memory.getMemory(size, i, hole);
}
}
System.err.println("OUT OF MEMORY!");
return memory;
}
2.循环首次适应算法
public Memory 循环首次适应算法(Memory memory, int size) {
Hole hole = memory.getHoles().get(memory.getLastFind());
//判断最后寻址的分区的大小是否足够
if (hole.isFree() && hole.getSize() >= size) {
return memory.getMemory(size, memory.getLastFind(), hole);
}
int length = memory.getHoles().size();
int sum = 0; //为区分与首次适应算法循环次数所设置
//如果不够,则从下一个分区开始循环
for (int i = (memory.getLastFind() + 1) % length; i != memory.getLastFind(); i = (i + 1) % length) {
sum++;
memory.setLastFind(i);
hole = memory.getHoles().get(i);
if (hole.isFree() && hole.getSize() >= size) {
System.out.println("查找" + sum + "次");
return memory.getMemory(size, i, hole);
}
}
System.err.println("OUT OF MEMORY!");
return memory;
}
3.最佳适应算法
public Memory 最佳适应算法(Memory memory, int size) {
int findIndex = -1; //最佳分区的下标
int min = memory.getSize(); //min存储当前找到的最小的合适的分区大小
for (int i = 0; i < memory.getHoles().size(); i++) {
//memory.setLastFind(i);
Hole hole = memory.getHoles().get(i);
if (hole.isFree() && hole.getSize() >= size) {
//若当前找到的分区大小比min还要合适(剩余空间更小),则修改其值
if (min > hole.getSize() - size){
min = hole.getSize() - size;
findIndex = i;
}
}
}
if (findIndex != -1) { //若存在合适分区
return memory.getMemory(size, findIndex, memory.getHoles().get(findIndex));
}
System.err.println("OUT OF MEMORY!");
return memory;
}
4.最坏适应算法
public Memory 最坏适应算法(Memory memory, int size) {
int findIndex = -1;
int max = 0;
for (int i = 0; i < memory.getHoles().size(); i++) {
Hole hole = memory.getHoles().get(i);
if (hole.isFree() && hole.getSize() >= size) {
if (max < hole.getSize() - size){
max = hole.getSize() - size;
findIndex = i;
}
}
}
if (findIndex != -1) {
return memory.getMemory(size, findIndex, memory.getHoles().get(findIndex));
}
System.err.println("OUT OF MEMORY!");
return memory;
}
1.初始化大小为500的内存并初始化大小为100 50 40 200 100的进程
2.申请两个最小内存(5)释放内存(2号分区)
3.释放相邻尾内存
4.释放相邻首内存
5.最佳适应算法与最坏适应算法(进程大小为5)
6.首次适应算法与循环首次适应算法(进程大小为50)
项目源代码已上传至GitHub:https://github.com/xhy1999/operation-system