实验一《CPU Scheduling》
实验学时: 4 实验地点: 第二综合楼204 实验日期: 2022/10/25
一、实验目的
多道系统中,当就绪进程数大于处理机数时,须按照某种策略决定哪些进程优先占用处理机。本实验模拟实现处理机调度,加深了解处理机调度的工作过程。
- 实验内容
选择一个调度算法,实现处理机调度。
1、设计一个按优先权调度算法实现处理机调度的程序;
2、设计按时间片轮转实现处理机调度的程序。
三、实验方法
1)构建PCB,内容涵盖:
进程名/PID;
要求运行时间(单位时间);
优先权;
到达时间:
状态:
PCB指针;
- 设计两种调度算法:抢占式优先级调度,时间片轮转法调度;
- 设计随机创建进程,挂起,解挂,切换调度算法功能;
- 使用工具为QT,采用图形界面,动态展示调度过程当中各进程及队列变化。
四、实验过程
1、需求分析:
1)可自行编辑创建进程,也可随机创建进程,后备队列中按照到达时间排序,选择最多六个进程进入就绪队列,优先级调度算法时就绪队列中按照优先级进行排序;
2)按照不同的算法进行不同的调度方法:抢占式优先级调度:从就绪队列选择优先级最高的进程进行运行,每运行一个时间片后,优先级减一,运行时间减一,当运行时间为0时,进程运行完成;时间片轮转法调度:按照先到先运行法则,每运行一个时间片后退出运行,换另外一个进程运行,运行时间为0,则进程运行完成;
3)使用QT完成图形界面;
4)设置挂起和解挂功能;
5)可动态增加进程;
6)如果内存中进程数少于规定道数,可自动从后备队列通过作业调度选择一作业进入,作业调度算法可自行选择;
7)被挂起进程入挂起队列,设置解挂功能用于将指定挂起进程解挂并入就绪队列;
8)每次调度后,显示各进程状态,采用进度条形式展现。
2、概要设计:
1)进行UI设计:添加进程、随机添加进程、backup队列、ready队列、running队列、suspend队列、进程信息显示框;
2)设置添加创建进程按钮,随机创建进程按钮,用户可输入进程名称,运行时间,到达时间,优先级从而添加到backup队列中,或者直接随机创建多个进程添加到backup队列中;
3)设置挂起和解挂按钮,用户选中ready队列或running队列中进程,点击挂起即可将该进程放入suspend队列中,选中挂起队列中的进程,点击解挂即可将该进程放入ready队列中;4)设置运行按钮,程序一直处于运行状态,直到所有进程完成或者点击暂停按钮;
5)设置暂停按钮,将处于一直运行的程序暂停;
6)设置信息显示框,显示所有信息的各个状态变化,运行时长等信息;
7)设置算法调度按钮,不同的按钮代表不同的算法;
8)自定义item加入QTableWidget中显示数据,设置进度条,展示进程完成百分比;
9)优化界面将界面做的更加美观
3、详细设计:
1)图形界面使用QTableWidget、QLable、QPushButton、QProgerssBar、QTextLine、QText完成图形化设计;
2)考虑算法思路,定义PCB类,声明必要属性,以及get&set方法;
3)设置运行按钮,使用QTimer计时器,程序每秒运行一次;
4)设置挂起功能,用户点击ready队列中的进程,获得该进程的信息,点击挂起即可将该进程挂起,信息转移到suspend队列中;
5)设置解挂功能,用户点击suspend队列种的进程,获得该进程的信息,点击解挂即可将该进程放入ready队列,再根据算法进行排序。
五、实验代码
1)PCB类:
class PCB
{
private:
QString PID;
QString status;
int priority;
int runtime;
int max_runtime;
PCB *pcb;
public:
PCB();
PCB(QString PID,QString status,int priority,int runtime);
void setRuntime(int value);
void delRuntime(int value);//进程运行时间减少value
void setStatus(QString value);
void setPID(QString value);
void setMaxRuntime(int value);//设置进程需要的总运行时间
void setPriority(int value);
void delPriority(int value);//进程优先级权值减少value
void addPriority(int value);//进程优先级权值增加value
void setDefaultPCB();
QString getPID() const;
int getRuntime() const;
int getMaxRuntime() const;
QString getStatus() const;
int getPriority() const;
};
- 每秒更新一次的优先级调度算法
void Widget::refreshProcs_SJF()
{
ui->lab_CPUScheInfo->setText("调度中...");
//取运行进程的优先级、就绪队列第一个进程的优先级
priority_run = runningProcs->getPriority();
priority_ready1st = ready[0].getPriority();
//如果内存中进程数不超过6且后备队列第一条数据不为空,则自动从后备队列中添加进程
if(backup[0].getPriority()!=0)
{
for(;processNum<NUM_OF_PROCESS;)
{
if(backup[0].getPriority()==0)
{
break;
}
firstOfBackToReady();
delFirstOfBackup();
backupToUi();
}
}
//进程抢占:当就绪队列第一条数据的优先级大于运行进程时,注意顺序不要搞错
if(priority_ready1st > priority_run)
{
ui->lab_CPUScheInfo->setText("发生抢占");
//被抢占的进程重新加入就绪队列
runningToReady();
//就绪队列第一个进程进入运行队列
firstOfReadyToRunningUi();
runningToUi();
//在就绪队列中删除第一个进程
delFirstOfReady();
readyToUi();
//对就绪队列进行排序,更新界面
bubbleSort(ready);
readyToUi();
}
//运行中的进程优先级-1,运行时间-1,运行时间==0时修改状态,输出在进程信息TableWidget
if(runningProcs->getPriority()!=0 && runningProcs->getRuntime()-1!=0)
{
runningProcs->delPriority(1);
runningProcs->delRuntime(1);
//刷新ui界面上的运行调度ui界面
runningToUi();
} else {
runningProcs->setStatus("终止");
//在进程调度信息处输出终止进程的信息
finishToInfo();
//就绪队列第一个进程加入RunningProcs中
firstOfReadyToRunningUi();
//在就绪队列中删除该条信息
delFirstOfReady();
//给就绪进程数组重新排序
bubbleSort(ready);
//刷新就绪列表ui界面
readyToUi();
}
//就绪队列中等待的进程优先级+1
for(int i = 0;i<NUM_OF_PROCESS&&ready[i].getPriority()!=0;i++)
{
ready[i].addPriority(1);
}
//刷新就绪列表ui界面
readyToUi();
//刷新挂起列表ui界面
suspendToUi();
}
- 每时间片更新一次的时间片轮转法
void Widget::refreshProcs_RR()
{
ui->lab_CPUScheInfo->setText("调度中...");
//如果内存中进程数不超过6且后备队列第一条数据不为空,则自动从后备队列中添加进程
if(backup[0].getPriority()!=0)
{
for(;processNum<NUM_OF_PROCESS;)
{
if(backup[0].getPriority()==0)
{
break;
}
firstOfBackToReady();
delFirstOfBackup();
backupToUi();
readyToUi();
}
}
//每隔1s调用一次refreshProcs_RR函数,运行中进程运行时间一次-1,时间片为3
if(sliceTime<TIMESLICE)
{
if(runningProcs->getRuntime()-1==0)//进程终止后
{
runningProcs->setStatus("终止");
//在进程调度信息处输出终止进程的信息
finishToInfo();
//就绪队列第一个进程加入RunningProcs中
firstOfReadyToRunningUi();
//在就绪队列中删除第一条信息
delFirstOfReady();
//刷新就绪列表ui界面
readyToUi();
}
sliceTime++;
runningProcs->delRuntime(1);
//刷新ui界面上的运行调度ui界面
runningToUi();
} else {//一次时间片之后,进程仍未结束,则将该进程重新加入到就绪队列的最后一条
//将运行的进程加入到就绪度列最后一条
runningToReady();
//就绪队列第一条进程进入运行列表
firstOfReadyToRunningUi();
//删除就绪队列第一条数据
delFirstOfReady();
//刷新就绪队列ui界面
readyToUi();
sliceTime = 0;
}
}
4)进程挂起
void Widget::receiveRunningToSuspend(QString PID)
{
qDebug()<<"receiveRunningToSuspend...";
//将运行进程加入挂起队列
workToSuspend(*runningProcs);
//更新挂起队列的ui界面
suspendToUi();
//将就绪队列第一条数据加入到运行列表
firstOfReadyToRunningUi();
//删除就绪队列第一条数据
delFirstOfReady();
//刷新就绪队列界面
readyToUi();
}
5)进程解挂
void Widget::receiveSuspendToReady(QString PID)
{
qDebug()<<"被解挂的进程PID:"+PID;
for(int i = 0;i<NUM_OF_PROCESS;i++)
{
if(suspend[i].getPID() == PID)
{
qDebug()<<"receiveSuspendToReady...";
//将被解挂的进程加入就绪队列
suspendToReady(suspend[i]);
//给就绪数组排序
bubbleSort(ready);
//更新就绪队列的ui界面
readyToUi();
//删除挂起队列中的该进程
delOneOfSuspend(PID);
//更新挂起队列的ui界面
suspendToUi();
break;
}
}
}
- 实验结论
添加作业
选择调度算法
- 优先级调度
开始调度
挂起、解挂进程
2)时间片轮转法
开始调度
七、实验小结
1)UI界面:花了有一段时间设计界面,综合考虑后自定义了进程item,与QStandardItem绑定后显示在QTableWidget中,加入了表单、进度条和按钮,使数据显示更加优美;
2)QTimer:设置了一秒更新一次的时间片,也可以让用户自己设置时间片,项目还不够完整,可以继续改进;
3)信号与槽:不同类之间的通信采用了信号与槽的connect方法,方便item、添加作业页面和选择调度算法页面与主页面的消息传递,Qt这一点很方便;
4)具体算法:把常用的方法都封装起来,便于后期对项目进行优化、升级时的维护,也便于撰写其他调度算法;
5)PCB实现:指针应用还是不够熟练,不能清晰得运用指针指向下一个进程的PCB,导致整个程序过于依赖内存,但考虑到这是轻量级项目,还能够原谅,但仍需改进。