操作系统
一、实验目的
进程调度是处理机管理的核心内容。本实验要求用高级语言编写模拟进程调度程序,以便加深理解有关进程控制块、进程队列等概念,并体会和了解优先数算法(包括抢占式和非抢占式)和时间片轮转算法的具体实施办法。
二、实验内容和要求
- 设计进程控制块PCB的结构,通常应包括如下信息:
进程名、进程优先数(时间片轮转算法中本次轮转需要的剩余时间片数)、进程已占用的CPU时间、进程到完成还需要的时间、进程的状态、当前队列指针等。
- 编写两种调度算法程序:
优先数调度算法程序(包括抢占式和非抢占式)
循环轮转调度算法程序
- 将程序源代码和运行截图写入实验报告并提交。
三、实验步骤
- 实验准备
(1) 查阅相关资料;
(2) 初步编写程序;
(3) 准备测试数据;
- 源代码
package caozuoxitong;
import java.util.Scanner;
class PCB {
String name; // 进程的名字
int prio; // 进程的优先级
int round; // 分配 CPU 的时间片
int cputime; // CPU 执行时间
int needtime; // 进程执行所需要的时间
char state; // 进程的状态, W——就绪, R——执行, F——完成
int count; // 记录执行的次数
PCB next; // 链表指针
PCB(String name, int needtime) {
this.name = name;
this.needtime = needtime;
this.cputime = 0;
this.state = 'W';
this.count = 0;
this.round = 2;
this.prio = 50 - needtime; // 设置进程优先值初值
}
}
public class test {
static PCB ready = null, run = null, finish = null, tail = null; // 定义三个队列,就绪队列头,执行队列头和完成队列头,就绪队列尾
static int N;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
char chose = ' ';
while (chose != 'q' && chose != 'Q') {
System.out.println("选择进程优先级算法(抢占式)请输入P,选择循环轮转算法请输入R,选择循环轮转算法(非抢占式)请输入I,退出请输入Q");
System.out.print("请输入你的选择:");
chose = scanner.next().charAt(0);
if (chose != 'q' && chose != 'Q') {
if (chose == 'P' || chose == 'p') {
priorInit(chose);
priority(chose);
} else if (chose == 'r' || chose == 'R') {
roundrunInit(chose);
roundrun(chose);
}
else if (chose == 'I' || chose == 'i') {
priorInit2(chose);
priority2(chose);
}
}
}
System.out.println("谢谢使用!");
scanner.close();
}
static void firstin() { // 调度就绪队列的第一个进程投入运行
if (ready != null) {
run = ready;
ready = ready.next;
run.state = 'R';
run.next = null;
} else {
run = null;
}
}
static void print1(char a) { // 打印表头行信息
if (Character.toUpperCase(a) == 'P') {
System.out.println("name cputime needtime prio state");
} else {
System.out.println("name cputime needtime count round state");
}
}
static void print2(char chose, PCB p) { // 打印每一行的状态信息
if (Character.toUpperCase(chose) == 'P') {
System.out.printf("%s\t%d\t%d\t%d\t%c\n", p.name, p.cputime, p.needtime, p.prio, p.state);
} else {
System.out.printf("%s\t%d\t%d\t%d\t%d\t%c\n", p.name, p.cputime, p.needtime, p.count, p.round, p.state);
}
}
static void print(char chose) { // 打印每执行一次算法后所有的进程的状态信息
PCB p;
print1(chose);
if (run != null) {
print2(chose, run);
}
p = ready;
while (p != null) {
print2(chose, p);
p = p.next;
}
p = finish;
while (p != null) {
print2(chose, p);
p = p.next;
}
}
static void insertPrio(PCB q) { // 在优先数算法中,将尚未完成的PCB按优先数顺序插入到就绪队列中
PCB p = ready, r = null;
if (q.prio > ready.prio) { // 要插入的进程的优先级大于ready 的优先级
q.next = ready;
ready = q;
} else { // 要插入的进程的优先级不大于ready 的优先级
while (p != null && p.prio >= q.prio) {
r = p;
p = p.next;
}
q.next = p;
if (r != null) {
r.next = q;
}
}
}
static void priorInit(char chose) { // 进程优先级法初始化将进程按优先级插入到就绪队列里
Scanner scanner = new Scanner(System.in);
System.out.println("输入进程的个数N:");
N = scanner.nextInt();
for (int i = 0; i < N; i++) {
System.out.printf("输入第%d个进程名\n", i + 1);
String na = scanner.next();
System.out.println("完成进程需要的时间片数");
int time = scanner.nextInt();
PCB p = new PCB(na, time);
if (ready == null) {
ready = p;
ready.next = null;
} else {
insertPrio(p);
}
System.out.println("当前就绪队列的进程的信息");
print(chose);
}
System.out.printf("%d个进程已按优先级从高到低进到就绪队列中\n", N);
System.out.println("按回车键开始模拟优先级算法....");
scanner.nextLine();
scanner.nextLine();
firstin();
}
static void priority(char chose) { // 进程优先级算法总函数
while (run != null) {
run.cputime += 1;
run.needtime -= 1;
run.prio -= 1;
if (run.needtime == 0) {
run.next = finish;
finish = run;
run.state = 'F';
run.prio = 0;
run = null;
firstin();
} else {
if (ready != null && run.prio < ready.prio) {
run.state = 'W';
insertPrio(run);
run = null;
firstin();
}
}
print(chose);
}
}
static void priorInit2(char chose) { // 进程优先级法初始化将进程按优先级插入到就绪队列里
Scanner scanner = new Scanner(System.in);
System.out.println("输入进程的个数N:");
N = scanner.nextInt();
for (int i = 0; i < N; i++) {
System.out.printf("输入第%d个进程名\n", i + 1);
String na = scanner.next();
System.out.println("完成进程需要的时间片数");
int time = scanner.nextInt();
PCB p = new PCB(na, time);
if (ready == null) {
ready = p;
ready.next = null;
} else {
insertPrio(p);
}
System.out.println("当前就绪队列的进程的信息");
print(chose);
}
System.out.printf("%d个进程已按优先级从高到低进到就绪队列中\n", N);
System.out.println("按回车键开始模拟优先级算法....");
scanner.nextLine();
scanner.nextLine();
firstin();
}
static void priority2(char chose) { // 进程优先级算法总函数
{
while (run != null) {
run.cputime += 1;
run.needtime -= 1;
run.prio -= 1;
if (run.needtime == 0) {
run.next = finish;
finish = run;
run.state = 'F';
run.prio = 0;
run = null;
firstin();
}
print(chose);
}
}
}
static void insertRr(PCB q) { // 在轮转法中,将执行了一个时间片单位(为2),但尚未完成的进程的PCB,插到就绪队列的队尾
tail.next = q;
tail = q;
q.next = null;
}
static void roundrunInit(char chose) { // 循环轮转法初始化将就绪队列保存为FIFO队列
Scanner scanner = new Scanner(System.in);
System.out.println("\t\t循环轮转算法模拟全过程\n\n");
System.out.println("输入进程的个数N:");
N = scanner.nextInt();
for (int i = 0; i < N; i++) {
System.out.printf("输入第%d个进程名\n", i + 1);
String na = scanner.next();
System.out.println("完成进程需要的时间片数");
int time = scanner.nextInt();
PCB p = new PCB(na, time);
if (ready != null) {
insertRr(p);
} else {
ready = p;
tail = p;
}
System.out.println("当前就绪队列的进程的信息");
print(chose);
}
System.out.printf("%d 个进程已按FIFO进到就绪队列中\n", N);
System.out.println("按回车键开始模循环轮转算法......");
scanner.nextLine();
scanner.nextLine();
run = ready;
ready = ready.next;
run.state = 'R';
}
static void roundrun(char chose) { // 循环轮转法总算法
while (run != null) {
run.cputime += 1;
run.needtime -= 1;
run.count += 1;
if (run.needtime == 0) {
run.next = finish;
finish = run;
run.state = 'F';
run.prio = 0;
run = null;
if (ready != null) {
firstin();
}
} else {
if (run.count == run.round) {
run.count = 0;
if (ready != null) {
run.state = 'W';
insertRr(run);
firstin();
}
}
}
print(chose);
}
}
}
- 测试数据
优先级算法(抢占式)
选择循环轮转算法
选择循环轮转算法(非抢占式)
- 运行结果
优先级算法(抢占式)
选择循环轮转算法
选择循环轮转算法(非抢占式)
四、遇到的主要问题和解决方法
问题:队列是先进先出的 , 在优先级算法中怎么来向插入新的进程 , 使其能够按优先级排序 。
解决方法: 使用链表可以完美解决。
问题: 低优先级的进程始终被高优先级的进程抢占,那么低优先级进程可能会长时间等待而无法执行,产生进程饥饿问题。
解决方法:
优先级衰减: 可以尝试提高他的优先级,知道可以执行。
五、实验总结
通过本次实验,学习了优先数调度算法程序(包括抢占式和非抢占式),循环轮转调度算法程序,对进程的调度有了更深的了解。优先级调度算法可以根据进程的优先级来确定执行顺序。在非抢占式算法中,进程一旦开始执行,将一直运行,而在抢占式算法中,更高优先级的进程可以随时抢占CPU。循环轮转调度算法中,每个进程被分配一个时间片来运行。时间片结束后,进程被放入就绪队列的末尾,调度器选择队列中的下一个进程来运行。在实际中,选择合适的算法,可以提高性能和用户的体验。