操作系统课程设计之时间片轮转调度算法模拟(Java版本可视化)
1. 课程设计要求
本次课程设计的题目是,时间片轮转调度算法的模拟实现。要求在充分理解时间片轮转调度算法原理的基础上,编写一个可视化的算法模拟程序。
具体任务如下:
(1)根据需要,合理设计PCB结构,以适用于时间片轮转调度算法;
(2)设计模拟指令格式,并以文件形式存储,程序能够读取文件并自动生成指令序列。
(3)根据文件内容,建立模拟进程队列,并能采用时间片轮转调度算法对模拟进程进行调度。
任务要求:
(1)进程的个数,进程的内容(即进程的功能序列)来源于一个进程序列描述文件。
(2)需将调度过程输出到一个运行日志文件。
(3)开发平台及语言不限。
(4)要求设计一个Windows可视化应用程序。
2. 课程设计说明
模拟指令的格式:操作命令+操作时间
● C : 表示在CPU上计算
● I : 表示输入
● O : 表示输出
● W : 表示等待
● H : 表示进程结束
操作时间代表该操作命令要执行多长时间(时间片个数)。这里假设I/O设备的数量没有限制,I和O设备都只有一类。
I,O,W三条指令实际上是不占有CPU的,执行这三条指令就应该将进程放入对应的等待队列(输入等待队列,输出等待队列 ,其他等待队列)。
进程名称 | 进程信息 |
---|---|
P1 | C10 I20 C40 I30 C20 O30 H00 |
P2 | I10 C50 O20 H00 |
P3 | C10 I20 W20 C40 O10 H00 |
以上表格内容为进程具体时间片安排信息,存放在prc.txt文件中等待读取。
3. 课程设计UI界面
此处时间片默认为(500)单位为ms
需求分析:
本小程序需要实现的功能以下可见
打开文件 选择文件 读取文件 时间片设置 开始调度 暂停调度 以及最后的日志输出
4. 预备知识
(1)编程需要掌握:
Java基本语法;
此处我用软件eclipse进行Java编程;
Java的swing使用插件 WindowsBulider插件进行UI界面的设计;
Java列表的使用;
Java线程的分配;
插件的安装教程见插件安装教程
(2)时间片轮转调度算法掌握:
(借鉴别人的博客,后面附有连接)
在轮转RR法中,系统根据FCFS策略,将所有的就绪进程排成一个就绪队列,并可设置每隔一定时间间隔(如30 ms)即产生一次中断, 激活系统中的进程调度程序, 完成一次调度,将CPU分配给队首进程,令其执行。当该进程的时间片耗尽或运行完毕时,系统再次将CPU分配给新的队首进程(或新到达的紧迫进程)。由此,可保证就绪队列中的所有进程在一个确定的时间段内,都能够获得次 CPU执行。
5. 关键代码
所有程序包括三个类(个人设计三个,随自己,也可以一个)
PCB类(存放文件读取来的进程的信息)
public class PCB {
private String pName;//进程名称
private List pInstructions = new ArrayList<Instructions>();//进程中的指令列表
private int CurrentInstruction; //当前运行指令索引
//后面自己添加其成员的get set方法
}
Instructions类(动态的获取进程的信息机更新进程信息)
private char IName; //指令类型
private double IRuntime; //指令运行时间
private double IRemainTime; //指令剩余运行时间
//后面自己添加get set 方法
//再添加一个时间片控制方法 减时间片
public void subIRemainTime(){
this.setIRemainTime(this.getIRemainTime()-1);
}
主类(关键部分,完成整个程序功能的实现,以及界面的展示)
//界面元素的定义
private JButton bFIle, bStart, bStop;
private JTextField tTime, tCur;
private JTextArea tAll, tReady, tInput, tOutput, tWait;//队列文本域
private File file;
//进程信息列表定义
private ArrayList<PCB> allQue = new ArrayList<PCB>();
private ArrayList<PCB> readyQue = new ArrayList<PCB>();
private ArrayList<PCB> inQue = new ArrayList<PCB>();
private ArrayList<PCB> outQue = new ArrayList<PCB>();
private ArrayList<PCB> waitQue = new ArrayList<PCB>();
private volatile Thread blinker;//Thread线程控制方法
private long count = 0;//计数器
在主类中定义其他关键方法
文件的读写方法
private void readFile() {
if (file != null) {
try {
BufferedReader in = new BufferedReader(new FileReader(file));
String str;
allQue.clear();
PCB pcb = null;
while ((str = in.readLine()) != null) {
if (str.charAt(0) == 'P') {
pcb = new PCB();
pcb.setpName(str);
} else {
Instructions instructions = new Instructions();
instructions.setIName(str.charAt(0));
instructions.setIRuntime(parseDouble(str.substring(1)));
instructions.setIRemainTime(instructions.getIRuntime());
assert pcb != null;
pcb.getpInstructions().add(instructions);
if (instructions.getIName() == 'H') {
//H代表当前进程结束,添加到就绪队列
allQue.add(pcb);
}
}
}
} catch (IOException e) {
System.out.println("文件读取错误!");
}
}
}
private void chooseFile() {
//选择文件
FileNameExtensionFilter filter = new FileNameExtensionFilter("*.txt", "txt");
JFileChooser jfc = new JFileChooser(".");//当前目录下
jfc.setFileFilter(filter);
jfc.setMultiSelectionEnabled(false);
jfc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
int result = jfc.showSaveDialog(null);
if (result == JFileChooser.APPROVE_OPTION) {
file = jfc.getSelectedFile();
}
}
界面按钮事件
bFIle.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
chooseFile();
readFile();
saveLog("读取文件成功!\r\n________________________\r\n");
showAll(allQue);
}
});
//开始按钮事件
bStart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (allQue.size()==0) {
JOptionPane.showMessageDialog(null, "请重新选择文件!",JOptionPane.INFORMATION_MESSAGE);
return;
}
if (boolTTime()) {
initQue();
startRun();
}
}
});
bStop.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
bStart.setText("继续调度");
blinker = null;
}
});
初始化队列
private void initQue() {
readyQue.clear();
inQue.clear();
outQue.clear();
waitQue.clear();
//分配排序队列
for (PCB p : allQue) {
if (p.getpInstructions().get(0).getIName() == 'C') {
readyQue.add(p);
}
else if (p.getpInstructions().get(0).getIName() == 'I') {
inQue.add(p);
}
else if (p.getpInstructions().get(0).getIName() == 'O') {
outQue.add(p);
}
else if (p.getpInstructions().get(0).getIName() == 'W') {
waitQue.add(p);
}
}
}
进程队列运转更新方法
private void runReady() {
if (readyQue.size() > 0) {
readyQue.get(0).getpInstructions().get(0).subIRemainTime();//调度首队列一次
tCur.setText(readyQue.get(0).getpName());
if (readyQue.get(0).getpInstructions().get(0).getIRemainTime() == 0) {
readyQue.get(0).getpInstructions().remove(0);
if (readyQue.get(0).getpInstructions().get(0).getIName() == 'C') {
readyQue.add(readyQue.get(0));//添加到ready末尾
} else if (readyQue.get(0).getpInstructions().get(0).getIName() == 'I') {
inQue.add(readyQue.get(0));//添加到in末尾
} else if (readyQue.get(0).getpInstructions().get(0).getIName() == 'O') {
outQue.add(readyQue.get(0));//添加到out末尾
} else if (readyQue.get(0).getpInstructions().get(0).getIName() == 'W') {
waitQue.add(readyQue.get(0));//添加到wait末尾
} else if (readyQue.get(0).getpInstructions().get(0).getIName() == 'H') {
allQue.remove(readyQue.get(0));
}
readyQue.remove(readyQue.get(0));
} else {
readyQue.add(readyQue.get(0));//移动到末尾
readyQue.remove(readyQue.get(0));
}
}
}
private void runIn() {
if (inQue.size() > 0) {
inQue.get(0).getpInstructions().get(0).subIRemainTime();//调度首队列一次
if (inQue.get(0).getpInstructions().get(0).getIRemainTime() == 0) {
inQue.get(0).getpInstructions().remove(0);
if (inQue.get(0).getpInstructions().get(0).getIName() == 'C') {
readyQue.add(inQue.get(0));//添加到ready末尾
} else if (inQue.get(0).getpInstructions().get(0).getIName() == 'I') {
inQue.add(inQue.get(0));//添加到in末尾
} else if (inQue.get(0).getpInstructions().get(0).getIName() == 'O') {
outQue.add(inQue.get(0));//添加到out末尾
} else if (inQue.get(0).getpInstructions().get(0).getIName() == 'W') {
waitQue.add(inQue.get(0));//添加到wait末尾
} else if (inQue.get(0).getpInstructions().get(0).getIName() == 'H') {
//说明该进程完成
allQue.remove(inQue.get(0));
}
inQue.remove(inQue.get(0));
} else {
inQue.add(inQue.get(0));//移动到末尾
inQue.remove(inQue.get(0));
}
}
}
private void runOut() {
if (outQue.size() > 0) {
outQue.get(0).getpInstructions().get(0).subIRemainTime();//调度首队列一次
if (outQue.get(0).getpInstructions().get(0).getIRemainTime() == 0) {
outQue.get(0).getpInstructions().remove(0);
if (outQue.get(0).getpInstructions().get(0).getIName() == 'C') {
readyQue.add(outQue.get(0));//添加到ready末尾
} else if (outQue.get(0).getpInstructions().get(0).getIName() == 'I') {
inQue.add(outQue.get(0));//添加到in末尾
} else if (outQue.get(0).getpInstructions().get(0).getIName() == 'O') {
outQue.add(outQue.get(0));//添加到out末尾
} else if (outQue.get(0).getpInstructions().get(0).getIName() == 'W') {
waitQue.add(outQue.get(0));//添加到wait末尾
} else if (outQue.get(0).getpInstructions().get(0).getIName() == 'H') {
if (allQue.size() > 0) {
allQue.remove(outQue.get(0));
}
}
outQue.remove(outQue.get(0));
} else {
outQue.add(outQue.get(0));//移动到末尾
outQue.remove(outQue.get(0));
}
}
}
private void runWait() {
if (waitQue.size() > 0) {
waitQue.get(0).getpInstructions().get(0).subIRemainTime();//调度首队列一次
if (waitQue.get(0).getpInstructions().get(0).getIRemainTime() == 0) {
waitQue.get(0).getpInstructions().remove(0);
if (waitQue.get(0).getpInstructions().get(0).getIName() == 'C') {
readyQue.add(waitQue.get(0));//添加到ready末尾
} else if (waitQue.get(0).getpInstructions().get(0).getIName() == 'I') {
inQue.add(waitQue.get(0));//添加到in末尾
} else if (waitQue.get(0).getpInstructions().get(0).getIName() == 'O') {
outQue.add(waitQue.get(0));//添加到out末尾
} else if (waitQue.get(0).getpInstructions().get(0).getIName() == 'W') {
waitQue.add(waitQue.get(0));//添加到wait末尾
} else if (waitQue.get(0).getpInstructions().get(0).getIName() == 'H') {
//说明该进程完成
allQue.remove(waitQue.get(0));
}
waitQue.remove(waitQue.get(0));
} else {
waitQue.add(waitQue.get(0));//移动到末尾
waitQue.remove(waitQue.get(0));
}
}
日志的生成方法(日志的生成在程序目录下当然也可以自己随意该位置的)
public void startRun() {
Runnable runnable = new Runnable() {
public void run() {
saveLog("------正在进行调度-------");
while (allQue.size() > 0 && blinker != null) {
try {
runReady();
runIn();
runOut();
runWait();
showAll(allQue);
saveLog("就绪队列:\t" + tAll.getText() + "\r\n");
showReady(readyQue);
saveLog("后备就绪队列:\t" + tReady.getText() + "\r\n");
showIn(inQue);
saveLog("输入队列:\t" + tInput.getText() + "\r\n");
showOut(outQue);
saveLog("输出队列:\t" + tOutput.getText() + "\r\n");
showWait(waitQue);
saveLog("等待队列:\t" + tWait.getText() + "\r\n");
saveLog("_______________________________");
count++;
System.out.println(count);
sleep(Long.parseLong(tTime.getText()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (allQue.size() == 0) {
bStart.setText("开始调度");
tCur.setText("");
saveLog("调度已完成");
} else {
saveLog("调度已停止");
}
}
};
blinker = new Thread(runnable);
blinker.start();
}
其他子方法
private void showAll(ArrayList<PCB> allQue) {
tAll.setText("");
for (PCB p : allQue) {
tAll.setText(tAll.getText() + "\r\n" + p.getpName());
}
}
private void showWait(ArrayList<PCB> waitQue) {
tWait.setText("");
for (PCB p : waitQue) {
tWait.setText(tWait.getText() + "\r\n" + p.getpName());
}
}
private void showOut(ArrayList<PCB> outQue) {
tOutput.setText("");
for (PCB p : outQue) {
tOutput.setText(tOutput.getText() + "\r\n" + p.getpName());
}
}
private void showIn(ArrayList<PCB> inQue) {
tInput.setText("");
for (PCB p : inQue) {
tInput.setText(tInput.getText() + "\r\n" + p.getpName());
}
}
private void showReady(ArrayList<PCB> que) {
tReady.setText("");
for (PCB p : que) {
tReady.setText(tReady.getText() + "\r\n" + p.getpName());
}
}
最后在main方法中新建一个主类,即可运行得到程序。(结构如下)
6. 结果演示
- 提供帮助
本程序的出处来自[此篇博客]感谢这个博主,真的很热心,但在其基础上进一步加以详细描述(适用于我一样的小白),应为当初我看到这篇博客的时候认为看到了救星,结过并不是想的那么详细。如果有一点java基础的同学,想必看到这篇文章后一定对你有所帮助,若没有基础,第一次碰java就别想了吧,就算拿到代码你也不一定跑的出来。
附java环境配置可以在CSDN上面找到相关教程(关键 JDK的配置,一定要加到环境变量中,eclipse程序方可打开)。
记住,每个人都不是一座孤岛,遇到困难先别放弃,先在网上找找,因为程序员最好的帮手,就应该有现如今发达的网络。