CPU调度算法 JAVA实现 附源码
OS实验课程
概述
CPU调度算法是操作系统中用于决定哪个进程可以获得CPU时间并在CPU上执行的算法。常见的CPU调度算法包括:
- 先来先服务(FCFS):按照作业到达的顺序依次执行,即先到达的作业先执行,后到达的作业后执行。
- 短作业优先(SJF):选择执行时间最短的作业优先执行,以减少平均等待时间,这种算法有抢占式和非抢占式的。
- 最高优先级优先(Priority Scheduling):根据作业的优先级确定执行顺序,优先级高的作业先执行。
- 时间片轮转(Round Robin):将CPU时间划分成多个时间片,每个作业依次执行一个时间片,如果作业在一个时间片内没有执行完,则被放到就绪队列的末尾等待下一次执行。
- 最短剩余时间优先(SRTF):在SJF的基础上,动态调整作业的执行顺序,选择剩余执行时间最短的作业优先执行。
- 多级反馈队列(Multilevel Feedback Queue Scheduling):将作业划分成多个队列,根据作业的特性和优先级选择对应的队列进行调度。
这里我用Java实现FCFS、SJF、RR、SRTF、MFQS这几种调度算法,图片只截取了部分运行结果,可以复制源码执行,查看完整打印的运行结果。
实现代码
主要有PCB、CPU两个类,PCB(Process Control Block,进程控制块)用来表示进程实体,PCB中保存着进程各种信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class PCB {
private int pid;
private char state;
private int priority;
private int arrival;
private int burst;
private int remain; public void subRest(){ this.remain--; }
@Override public String toString() { return "PCB"+this.pid; }
public void initPCB(int i){ this.pid = i+1; this.state = 'N'; this.priority = (int)(Math.random()*3+1); this.arrival = (int)(Math.random()*10); this.burst = (int)(Math.random()*10+1); this.remain = this.burst;
} }
|
CPU类负责执行各种调度算法,内部也有重要的公共属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| public class CPU { public static int progressNum = 5; public static int time = 0; public static int timeSlice = 2; public List<PCB> pcbList = new ArrayList<>(); public List<PCB> runList = new ArrayList<>(); public List<PCB> midRunList = new ArrayList<>(); public List<PCB> lowRunList = new ArrayList<>(); public void initProgress() { for (int i = 0; i < progressNum; i++) { PCB pcb = new PCB(); pcb.initPCB(i); pcbList.add(pcb); } } public void printPCBList() public void printRunList() public void printMultiQueueList()
public void process(PCB pcb, String type,String level) private void MFQS(PCB pcb,String level)
private void SJF(PCB pcb)
private void SRTF(PCB pcb) private void RR(PCB pcb)
public void FCFS(PCB pcb) public void run(String type) public void runMFQS(String type)
private boolean queueEmpty() private void processArrival() }
|
CPU类中的run和process方法
- run方法主要用来模拟计算机时钟,当我们的进程没有执行结束CPU就一直工作
processArrival
方法来模拟即将到来的进程,遍历初始化好的PCB列表,来选出到达时间和time一样的PCB,加入等待队列,同时设置PCB状态为W“等待”
process
方法获取等待运行队列的第一个进程,然后运行1个time,这里我用switch控制调度算法(具体算法后面说)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| public void run(String type) { while (!queueEmpty()) { processArrival(); if (!runList.isEmpty()) { process(runList.get(0), type,null); } time++; } }
private boolean queueEmpty() { for (PCB pcb : pcbList) { if (pcb.getState() != 'T') return false; } return true; }
private void processArrival() { for (int i = 0; i < progressNum; i++) { PCB pcb = pcbList.get(i); if (pcb.getArrival() == time) { pcb.setState('W'); runList.add(pcb); } } }
public void process(PCB pcb, String type,String level) { if (pcb != null) { switch (type) { case "FCFS": FCFS(pcb); break; case "RR": RR(pcb); break; case "SJF": SJF(pcb); break; case "SRTF": SRTF(pcb); break; case "MFQS": MFQS(pcb,level); break; } } }
|
FCFS先来先服务
FCFS(First-Come, First-Served)是一个最简单的进程调度算法,也被称为先来先服务调度算法。在FCFS调度算法中,进程按照它们到达的顺序被依次调度执行,即先到达的进程先被执行,直到进程执行完成。
优点:
- 简单直观:FCFS是一种非常简单和直观的调度算法,适用于简单的场景。
- 公平性:由于按照进程到达的顺序进行调度,因此FCFS保证了公平性,即先到达的进程先被执行。
- 非抢占:FCFS是一种非抢占式调度算法,一旦进程开始执行,直到完成或者阻塞,才会释放CPU资源。
缺点:
- 长等待时间:FCFS可能导致长作业等待时间,特别是当一个长作业在前面排队时,后面的短作业需要等待很长时间才能执行。
- 低响应性:由于FCFS是非抢占式的,一旦一个进程开始执行,其他进程需要等待它完成才能执行,可能导致低响应性。
- 不适合实时系统:FCFS无法满足实时系统对响应时间的要求,因为无法保证进程在规定的时间内被执行。
FCFS调度算法适用于简单的场景。
算法实现
这个很简单,设置PCB状态为R“运行”,剩余时间减1,判断是否执行结束
1 2 3 4 5 6 7 8
| public void FCFS(PCB pcb) { pcb.setState('R'); pcb.subRest(); if (pcb.getRemain() == 0) { pcb.setState('T'); runList.remove(0); } }
|
RR时间片轮转
RR(Round-Robin)是一种常见的进程调度算法,它是一种基于时间片的调度算法。在RR调度算法中,每个进程被分配一个时间片(时间量),当进程开始执行时,它将在一个时间片内执行,如果在时间片结束时进程还没有执行完成,则该进程会被暂停,放回就绪队列的末尾,等待下一次调度。被暂停的进程等待的时间片会在后续的调度中继续执行。
优点:
- 公平性:RR调度算法保证了每个进程都能获得执行的机会,每个进程都会被分配一个时间片,从而实现公平性。
- 高响应性:由于每个进程都有一个时间片,因此RR调度算法对于短作业和实时性要求较高的系统具有较好的响应性。
- 预防饥饿:由于进程被分配时间片,因此RR调度算法可以避免某些进程长时间等待,从而预防饥饿现象的发生。
缺点:
- 时间片长度选择:时间片的长度会影响系统的性能,如果时间片太短,会导致频繁的上下文切换,影响系统效率;如果时间片太长,可能会影响系统的响应速度。
- 高开销:由于需要频繁进行上下文切换,RR调度算法可能会带来一定的开销,特别是在进程数量较多时。
RR调度算法适用于需要保证公平性和响应性的系统,特别是对于短作业和实时性要求较高的场景。
算法实现
刚开始还是一样,设置PCB状态为R“运行”,剩余时间减1,判断是否执行结束,这里如果有进程执行结束需要恢复时间片为默认值,这里默认为2,否则假如上一个进程执行了一个时间就结束了,那么下一个进程只能执行一个时间,就会轮替掉。
重新入队时会添加到队列尾部
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| private void RR(PCB pcb) { pcb.setState('R'); pcb.subRest(); timeSlice--; if (pcb.getRemain() == 0) { pcb.setState('T'); runList.remove(0); timeSlice = 2; }
if (timeSlice == 0) { timeSlice = 2; if (pcb.getRemain() != 0) { runList.remove(0); pcb.setState('W'); runList.add(pcb); } } }
|
SJF最短任务优先、SRTF最短剩余时间优先
SJF(Shortest Job First)和SRTF(Shortest Remaining Time First)都是基于作业执行时间的调度算法,它们都优先选择执行时间最短的作业来执行。下面分别介绍它们的特点:
- SJF(Shortest Job First):
- SJF调度算法是一种非抢占式的调度算法,即一旦进程开始执行,直到完成或者阻塞,才会释放CPU资源。
- SJF根据进程的执行时间选择最短的作业来执行,以减少平均等待时间和周转时间。
- SJF算法可以分为两种:非抢占式SJF和抢占式SJF。非抢占式SJF是在进程到达时确定作业的执行时间,而抢占式SJF在进程执行过程中可以根据后续作业的到达情况重新调度。
- SJF调度算法可能导致长作业等待时间,特别是当一个长作业在前面排队时,后面的短作业需要等待很长时间才能执行。
- SRTF(Shortest Remaining Time First):
- SRTF调度算法是SJF的一种抢占式形式,即在进程执行过程中可以根据后续作业的到达情况重新调度。
- SRTF根据进程的剩余执行时间选择最短的作业来执行,因此可能会中断当前正在执行的作业,选择更短的作业执行。
- SRTF可以最大程度地减少作业的等待时间和周转时间,提高系统的效率和响应速度。
- SRTF调度算法可能会引起频繁的上下文切换,因为需要不断检查剩余执行时间最短的作业。
算法实现
这里我实现的SJF是一种抢占式算法,如果队列中加入的进程总执行时间最短,那么它会抢占当前的CPU,在实现这两个算法时我发现它们非常像,在代码中其实只是修改了一个判断条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| private void SJF(PCB pcb) { PCB shortRest = pcb; for (PCB item : runList) { if (pcb.getRemain() != 0 && pcb.getBurst() > item.getBurst()) { shortRest = item; runList.remove(shortRest); runList.add(0, shortRest); } } shortRest.setState('R'); shortRest.subRest(); if (shortRest.getRemain() == 0) { shortRest.setState('T'); runList.remove(0); } }
private void SRTF(PCB pcb) { PCB shortRest = pcb; for (PCB item : runList) { if (pcb.getRemain() != 0 && pcb.getRemain() > item.getRemain()) { shortRest = item; runList.remove(shortRest); runList.add(0, shortRest); } } shortRest.setState('R'); shortRest.subRest(); if (shortRest.getRemain() == 0) { shortRest.setState('T'); runList.remove(0); } }
|
MFQS多级反馈调节
MFQS(Multi-level Feedback Queue Scheduling)是一种多级反馈队列调度算法,常用于操作系统中的进程调度。MFQS将进程队列划分为多个级别,每个级别对应一个优先级,不同优先级的队列具有不同的时间片大小和调度策略。以下是MFQS调度算法的一些特点和工作原理:
- 多级反馈队列:MFQS将进程队列划分为多个级别,通常是三个或更多个级别。每个级别都有一个时间片大小,通常随着优先级的增加而减小。高优先级队列的时间片较短,低优先级队列的时间片较长。
- 调度策略:MFQS采用多级反馈的调度策略,即一个进程在不同队列之间切换执行。当一个进程在某个队列的时间片用完后,如果还未执行完,则会被移到下一个更低优先级的队列中继续执行。如果一个进程在高优先级队列等待时间较长,系统可能会提升其优先级,以提高其执行机会。
- 调度过程:MFQS的调度过程通常是按照优先级依次轮转执行各个队列中的进程。高优先级队列中的进程优先执行,如果一个进程在当前队列执行完毕,则进入下一个优先级更低的队列继续执行。这种方式可以保证高优先级的进程得到及时响应,同时也允许低优先级的进程有机会执行。
- 优点:MFQS能够平衡系统的响应速度和吞吐量,高优先级的进程能够快速响应,低优先级的进程则有机会执行,避免了饥饿现象。此外,MFQS还能够适应不同类型的工作负载,提高系统的整体性能。
算法实现
它的run方法特殊一点,因为有三个队列需要处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public void runMFQS(String type) { while (!queueEmpty()) { processArrival(); if (!runList.isEmpty()) { process(runList.get(0), type,"top"); } else if (!midRunList.isEmpty()) { process(midRunList.get(0), type,"mid"); } else if (!lowRunList.isEmpty()) { process(lowRunList.get(0), type,"low"); } time++; } }
|
我使用三个队列来实现,不同队列的时间片都是2,我这里没有对优先级进行特殊处理。你还可以对不同级别的队列设置不同的时间片,对等待时间超过一定阈值的进程把他提升到更高优先级的队列,以实现真正的“反馈调节”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| private void MFQS(PCB pcb,String level) { pcb.setState('R'); pcb.subRest(); timeSlice--; if (pcb.getRemain() == 0) { pcb.setState('T'); timeSlice = 2; switch (level){ case "top": runList.remove(0); break; case "mid": midRunList.remove(0); break; case "low": lowRunList.remove(0); break; } } if (timeSlice == 0) { timeSlice = 2; if (pcb.getRemain() != 0) { switch (level){ case "top": runList.remove(0); pcb.setState('W'); midRunList.add(pcb); break; case "mid": midRunList.remove(0); pcb.setState('W'); lowRunList.add(pcb); break; case "low": lowRunList.remove(0); pcb.setState('W'); lowRunList.add(pcb); break; } } } }
|
总结
通过学习CPU调度算法,可以更全面的认识操作系统,不同的CPU调度算法适用于不同的场景和需求,可以根据系统的特点和要求选择合适的调度算法,以提高系统的性能和效率。
其实这些算法大多也是来源于生活,像队列、抢占、优先级(特权),在写代码时应该多想想如果在现实中你会怎么做。
PCB.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| public class PCB {
private int pid;
private char state;
private int priority;
private int arrival;
private int burst;
private int remain;
public int getPid() { return pid; }
public void setPid(int pid) { this.pid = pid; }
public char getState() { return state; }
public void setState(char state) { this.state = state; }
public int getPriority() { return priority; }
public void setPriority(int priority) { this.priority = priority; }
public int getArrival() { return arrival; }
public void setArrival(int arrival) { this.arrival = arrival; }
public int getBurst() { return burst; }
public void setBurst(int burst) { this.burst = burst; }
public int getRemain() { return remain; }
public void setRemain(int remain) { this.remain = remain; } public void subRest(){ this.remain--; }
@Override public String toString() { return "PCB"+this.pid; }
public void initPCB(int i){ this.pid = i+1; this.state = 'N'; this.priority = (int)(Math.random()*3+1); this.arrival = (int)(Math.random()*10); this.burst = (int)(Math.random()*10+1); this.remain = this.burst;
} }
|
CPU.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
| public class CPU { public static int progressNum = 5; public static int time = 0; public static int timeSlice = 2; public List<PCB> pcbList = new ArrayList<>(); public List<PCB> runList = new ArrayList<>(); public List<PCB> midRunList = new ArrayList<>(); public List<PCB> lowRunList = new ArrayList<>();
public void initProgress() { for (int i = 0; i < progressNum; i++) { PCB pcb = new PCB(); pcb.initPCB(i); pcbList.add(pcb); } }
public void printPCBList() { System.out.println("进程号 进程状态 优先级 到达时间 CPU区间时间 剩余时间"); for (int i = 0; i < progressNum; i++) { PCB pcb = pcbList.get(i); System.out.println(pcb.getPid() + " " + pcb.getState() + " " + pcb.getPriority() + " " + pcb.getArrival() + " " + pcb.getBurst() + " " + pcb.getRemain()); } }
public void printRunList() {
for (int i = 0; i < runList.size(); i++) { PCB pcb = runList.get(i); System.out.print("进程号:" + pcb.getPid()); System.out.print(" 剩余时间:" + pcb.getRemain()); System.out.print(" | "); } System.out.println(); } public void printMultiQueueList() { System.out.print("一级队列 ===>>"); for (int i = 0; i < runList.size(); i++) { PCB pcb = runList.get(i); System.out.print("进程号:" + pcb.getPid()); System.out.print(" 剩余时间:" + pcb.getRemain()); System.out.print(" | "); } System.out.println(); System.out.print("二级队列 ===>>"); for (int i = 0; i < midRunList.size(); i++) { PCB pcb = midRunList.get(i); System.out.print("进程号:" + pcb.getPid()); System.out.print(" 剩余时间:" + pcb.getRemain()); System.out.print(" | "); } System.out.println(); System.out.print("三级队列 ===>>"); for (int i = 0; i < lowRunList.size(); i++) { PCB pcb = lowRunList.get(i); System.out.print("进程号:" + pcb.getPid()); System.out.print(" 剩余时间:" + pcb.getRemain()); System.out.print(" | "); } System.out.println(); }
public void process(PCB pcb, String type,String level) { if (pcb != null) { switch (type) { case "FCFS": FCFS(pcb); break; case "RR": RR(pcb); break; case "SJF": SJF(pcb); break; case "SRTF": SRTF(pcb); break; case "MFQS": MFQS(pcb,level); break; }
} else {
} }
private void MFQS(PCB pcb,String level) { pcb.setState('R'); pcb.subRest(); timeSlice--; if (pcb.getRemain() == 0) { pcb.setState('T'); timeSlice = 2; switch (level){ case "top": runList.remove(0); break; case "mid": midRunList.remove(0); break; case "low": lowRunList.remove(0); break; }
} if (timeSlice == 0) { timeSlice = 2; if (pcb.getRemain() != 0) { switch (level){ case "top": runList.remove(0); pcb.setState('W'); midRunList.add(pcb); break; case "mid": midRunList.remove(0); pcb.setState('W'); lowRunList.add(pcb); break; case "low": lowRunList.remove(0); pcb.setState('W'); lowRunList.add(pcb); break; }
} } }
private void SJF(PCB pcb) { PCB shortRest = pcb; for (PCB item : runList) { if (pcb.getRemain() != 0 && pcb.getBurst() > item.getBurst()) { shortRest = item; runList.remove(shortRest); runList.add(0, shortRest); System.out.println("--------------------"); System.out.println("进程:"+shortRest.getPid()+" 发生抢占 |"); } } shortRest.setState('R'); shortRest.subRest(); if (shortRest.getRemain() == 0) { shortRest.setState('T'); runList.remove(0); }
}
private void SRTF(PCB pcb) { PCB shortRest = pcb; for (PCB item : runList) { if (pcb.getRemain() != 0 && pcb.getRemain() > item.getRemain()) {
shortRest = item; runList.remove(shortRest); runList.add(0, shortRest); System.out.println("--------------------"); System.out.println("进程:"+shortRest.getPid()+" 发生抢占 |"); } } shortRest.setState('R'); shortRest.subRest(); if (shortRest.getRemain() == 0) { shortRest.setState('T'); runList.remove(0); }
}
private void RR(PCB pcb) { pcb.setState('R'); pcb.subRest(); timeSlice--; if (pcb.getRemain() == 0) { pcb.setState('T'); runList.remove(0); timeSlice = 2; }
if (timeSlice == 0) { timeSlice = 2; if (pcb.getRemain() != 0) { runList.remove(0); pcb.setState('W'); runList.add(pcb); } } }
public void FCFS(PCB pcb) { pcb.setState('R'); pcb.subRest(); if (pcb.getRemain() == 0) { pcb.setState('T'); runList.remove(0); } }
public void run(String type) { while (!queueEmpty()) { System.out.println("-------------------------time:--" + time + "------------------------------------------------------------------------------------"); processArrival(); if (!runList.isEmpty()) { printRunList(); process(runList.get(0), type,null); } time++; } } public void runMFQS(String type) { while (!queueEmpty()) { System.out.println("-------------------------time:--" + time + "------------------------------------------------------------------------------------"); processArrival();
if (!runList.isEmpty()) { process(runList.get(0), type,"top"); printMultiQueueList(); } else if (!midRunList.isEmpty()) { process(midRunList.get(0), type,"mid"); printMultiQueueList(); } else if (!lowRunList.isEmpty()) { process(lowRunList.get(0), type,"low"); printMultiQueueList(); } time++; } }
private boolean queueEmpty() { for (PCB pcb : pcbList) { if (pcb.getState() != 'T') return false; } return true; }
private void processArrival() { for (int i = 0; i < progressNum; i++) { PCB pcb = pcbList.get(i); if (pcb.getArrival() == time) { pcb.setState('W'); runList.add(pcb); } } }
public static void main(String[] args) { CPU cpu = new CPU(); cpu.initProgress(); cpu.printPCBList(); cpu.run("FCFS");
cpu.printPCBList();
} }
|