目录
一、实验目的(目的与任务)
目的:了解并掌握动态高优先权优先调度算法的理论,掌握动态优先权的设置方式。
任务:模拟实现动态高优先权优先的调度(若数值越大优先权越高,每运行一个时间单位优先权-n,若数值越小优先权越高,每运行一个时间单位优先权+n)。
二、实验内容(内容、要求与安排方式)
实验学时:2 实验类型:综合型
1.实验内容与要求:
(1)实验内容
设置进程体:进程名,进程的到达时间,服务时间,初始优先权,进程状态(W——等待,R——运行,F——完成),进程间的链接指针;
进程初始化:由用户输入进程名、服务时间、初始优先权进行初始化,同时,初始化进程的状态为W;
显示函数:在进程调度前、调度中和调度后进行显示;
排序函数:对就绪状态的进程按照优先权排序。优先权相同时进入等待队列时间早的进程在前,注意考虑到达时间;
调度函数:每次从等待队列队首调度优先权最高的进程执行,状态变化。并在执行一个时间单位后优先权变化,服务时间变化,状态变化。当服务时间为0时,状态变为F;
删除函数:撤销状态为F的进程。
(2)实验要求
①测试数据可以随即输入或从文件中读入;
②必须要考虑到进程的到达时间;
③最终能够计算每一个进程的周转时间、带权周转时间。
2.实验安排方式:
上机。
3.实验设备
1.所用设备:装有C/C++/JAVA等编程环境的计算机。
2.消耗性器材:无。
三、实验代码
①首先需要定义一个作业结构体,并在结构体内部设置状态变量
struct Job // 动态高优先权优先调度
{
int pid;
int startTime; //到达时间
int readTime; //进入准备队列的时间
int runTime; //运行时间
int serverTime; //服务时间
int status; //0==等待,1==运行,2==完成
int weight; //优先权,值越大,优先权越高
int finishTime; //完成时间
} job[100001];
②编写按照到达时间排序的函数
bool cmp(Job a, Job b)
{
return a.startTime < b.startTime;
}
sort(job, job + n, cmp); //按到达时间排序(主函数调用排序函数)
③设置表示所有作业数量与单位时间权重变化的全局变量
int n; //所有作业数量
int x; //每过一个时间单位权重的变化
④编写更改进程状态的函数
void updateStatus(int id, int time, int sta, int run, int we, int read) //修改进程状态
{
for (int i = 0; i < n; i++)
{
if (job[i].pid == id)
{
job[i].weight = we;
job[i].runTime = run;
job[i].finishTime = time;
job[i].status = sta;
job[i].readTime = read;
break;
}
}
}
⑤输入实验数据(进程的各个状态量)
printf("请输入进程数量:");
scanf("%d", &n);
printf("请依次输入进程ID、进程到达时间、进程服务时间、进程初始权重:\n");
for (int i = 0; i < n; i++)
{
scanf("%d %d %d %d", &job[i].pid, &job[i].startTime, &job[i].serverTime, &job[i].weight);
job[i].status = 0;
job[i].runTime = 0;
job[i].readTime = job[i].startTime;
}
printf("请输入每过一个时间单位权值的变化:");
scanf("%d", &x);
⑥动态高优先权优先调度算法的具体实现
sort(job, job + n, cmp); //按到达时间排序
Job text; //临时队首的进程作业
int nowJob = 0; //已经进入准备队列的进程数量
for (int i = 0;; i++) //时间刻度,当nowJob==n && 准备队列为空时跳出循环
{
printf("\n第%d时刻:\n", i);
for (int j = nowJob; j < n; j++) //如果开始时刻等于当前时刻,将作业压入准备队列
if (job[j].startTime == i && job[i].status == 0)
ReadQueue.push(job[j]), nowJob++, text.readTime = i;
else
break;
if (!ReadQueue.empty()) //如果当前时刻准备队列不为空
{
text = ReadQueue.top(); //取出当前准备队列队首并移除
ReadQueue.pop();
//当前队首进行工作,工作一个时间单位之后,权值变化并重新进入准备队列
text.runTime++; //运行时间+1;
text.status = 1; //状态设为正在运行
printf("正在运行的进程ID:%d号进程\n", text.pid);
if (text.runTime == text.serverTime) //如果运行时间==服务时间,就结束
{
text.finishTime = i + 1; //完成时间
//这里完成时间为i+1,以0时刻开始,运行一个单位,那么在这里就结束了
//但是理论的结束时间应该为后一个时间单位,所以为i+1
text.status = 2; //状态设为2
text.readTime = i + 1;
updateStatus(text.pid, text.finishTime, text.status, text.runTime, text.weight, text.readTime);
continue; //下一时刻
}
else //如果没有结束
{
text.weight -= x; //权重减少
text.readTime = i + 1; //重新进入队列的时间
ReadQueue.push(text); //重新进入准备队列
updateStatus(text.pid, text.finishTime, text.status, text.runTime, text.weight, text.readTime); //修改进程状态
}
}
for (int j = 0; j < n; j++)
{
printf("id:%d\t上次进入队列时刻:%d\t状态:%d\t运行时间:%d\t当前权重%d\t结束时间:%d\n", job[j].pid, job[j].readTime, job[j].status, job[j].runTime, job[j].weight, job[j].finishTime);
}
if (ReadQueue.empty() && nowJob == n) //如果队列为空并且所有进程作业都已入队,说明完成
break; //跳出循环
}
⑦计算作业的各个时间值,并输出
- 完成时间=开始时间+服务时间
- 周转时间=完成时间-到达时间
- 带权周转时间=周转时间/服务时间
printf("\n进程名\t到达时\t完成时\t周转时\t带权周转时\n");
double avg_sum_round_time = 0.0; //平均周转时间
double avg_sum_avg_time = 0.0; //平均带权周转时间
for (int i = 0; i < n; i++)
{
printf("%d\t%d\t%d\t%d\t%.2f\n", job[i].pid, job[i].startTime, job[i].finishTime, job[i].finishTime - job[i].startTime, 1.00 * (job[i].finishTime - job[i].startTime) / job[i].serverTime);
avg_sum_round_time += (job[i].finishTime - job[i].startTime);
avg_sum_avg_time += 1.00 * (job[i].finishTime - job[i].startTime) / job[i].serverTime;
}
printf("平均周转时间:%.2f\n", avg_sum_round_time / n);
printf("平均带权周转时间:%.2f\n", avg_sum_avg_time / n);
⑧使用文件操作读取实验数据
#include <stdio.h> // freopen函数所需头文件
freopen("C:\\Users\\lwx\\Desktop\\data.txt", "r", stdin);
5
1 0 6 3
2 1 4 3
3 2 1 4
4 4 1 5
5 8 3 4
0
将程序打包为exe文件
codeblocks下 写程序,先要建立一个工程。写好程序后,选择Build(构建),在工程目录里面就有个Debug或者Release的文件夹,里面就是打包好的exe文件。
![]()
codeblocks生成的exe文件,打开闪一下就没了,如何解决?答:加一句暂停语句:system("pause");。
解析:exe程序,就是这样的。自动退出,可以加一句暂停语句:system("pause");。
实验代码1
//https://blog.csdn.net/weixin_44606097/article/details/106044658
#include <stdio.h>
#include <fstream>
#include <queue>
#include <algorithm>
using namespace std;
const int jobNum_Max = 10000;
struct jobInfo
{
int uid; //进程号
int arrival_time; //到达时间
int service_time; //服务时间
int ready_time; //进入就绪队列时间
int remaining_time; //剩余服务时间
int finish_time; //完成时间
int weight; //权重
char status; //状态
friend bool operator<(jobInfo a, jobInfo b)
{
if (a.weight == b.weight)
return a.ready_time > b.ready_time; //时间小的优先
return a.weight < b.weight; //权值大的优先
}
} job[jobNum_Max];
int in_it(); //初始化,返回作业总数
bool cmp(jobInfo a, jobInfo b);
void solve();
void updateTojob(jobInfo t);
priority_queue<jobInfo> updateToWaitList();
void Show_Before(); //初始信息
priority_queue<jobInfo> Show_Ing(jobInfo t); //每个时刻作业的情况
void Show_After(); //调度图
int TotalNum, CurrentTime; //作业总数量、模拟时间片前进
int WeightChange;
int record[jobNum_Max]; //每个时间片处理的作业名
priority_queue<jobInfo> WaitList;
int main()
{
TotalNum = in_it();
Show_Before();
solve();
Show_After();
}
int in_it()
{
int flag;
printf("请选择输入方式(控制台输入选择 1,文件输入选择 2): ");
scanf("%d", &flag);
while (flag != 1 && flag != 2)
{
printf("输入错误,请重新输入:\n");
scanf("%d", &flag);
}
int n = 0;
if (flag == 1)
{
printf("请按照格式输入:“进程ID 进程到达时间 进程服务时间 进程初始权重”,按“Ctrl+Z”结束输入!\n");
while (scanf("%d%d%d%d", &job[n].uid, &job[n].arrival_time, &job[n].service_time, &job[n].weight) != EOF)
{
job[n].status = 'W';
job[n].remaining_time = job[n].service_time;
job[n].ready_time = 0;
n++;
}
}
else
{
ifstream date;
date.open("date.txt");
while (!date.eof())
{
date >> job[n].uid >> job[n].arrival_time >> job[n].service_time >> job[n].weight;
job[n].status = 'W';
job[n].remaining_time = job[n].service_time;
job[n].ready_time = 0;
n++;
}
printf("数据读取成功!\n");
}
sort(job, job + n, cmp);
printf("请输入每过一个时间单位权值的变化:");
scanf("%d", &WeightChange);
return n;
}
bool cmp(jobInfo a, jobInfo b)
{
return a.arrival_time < b.arrival_time;
}
void solve()
{
int InJobNum = 0;
jobInfo Running;
Running.uid = -1;
while (InJobNum != TotalNum || !WaitList.empty())
{
for (int i = InJobNum; i < TotalNum; i++)
{
if (job[i].arrival_time == CurrentTime)
{
job[i].ready_time = CurrentTime;
WaitList.push(job[i]);
InJobNum++;
}
else
break;
}
if (!WaitList.empty())
{
Running = WaitList.top();
WaitList.pop();
Running.status = 'R';
WaitList = Show_Ing(Running);
Running.remaining_time--;
record[CurrentTime++] = Running.uid;
if (!Running.remaining_time)
{
Running.finish_time = CurrentTime;
Running.status = 'F';
updateTojob(Running);
}
else
{
Running.weight -= WeightChange;
Running.ready_time = CurrentTime;
Running.status = 'W';
WaitList.push(Running);
updateTojob(Running);
}
}
else
{
Running.uid = -1;
WaitList = Show_Ing(Running);
record[CurrentTime++] = -1;
}
//WaitList = updateToWaitList();
}
Running.uid = -1;
WaitList = Show_Ing(Running);
}
void updateTojob(jobInfo t)
{
for (int i = 0; i < TotalNum; i++)
{
if (job[i].uid == t.uid)
{
job[i] = t;
return;
}
}
}
priority_queue<jobInfo> updateToWaitList()
{
priority_queue<jobInfo> t;
while (!WaitList.empty())
{
jobInfo a = WaitList.top();
WaitList.pop();
a.weight += WeightChange;
t.push(a);
updateTojob(a);
}
return t;
}
void Show_Before()
{
putchar('\n');
printf("%s\n", "进程信息");
printf("%s\t%s\t%s\t%s\n", "Id", "arrival", "service", "weight");
for (int i = 0; i < TotalNum; i++)
printf("%d\t%d\t%d\t%d\n", job[i].uid, job[i].arrival_time, job[i].service_time, job[i].weight);
}
priority_queue<jobInfo> Show_Ing(jobInfo t)
{
priority_queue<jobInfo> Temp;
putchar('\n');
printf("%s\t%s\t%s\t%s\t%s\t%s=%d\n", "Id", "remain", "status", "weight", "ready", "Time", CurrentTime);
if (t.uid != -1)
printf("%d\t%d\t%c\t%d\t%d\n", t.uid, t.remaining_time, t.status, t.weight, t.ready_time);
while (!WaitList.empty())
{
jobInfo a = WaitList.top();
WaitList.pop();
printf("%d\t%d\t%c\t%d\t%d\n", a.uid, a.remaining_time, a.status, a.weight, a.ready_time);
Temp.push(a);
}
for (int i = 0; i < TotalNum; i++)
if (job[i].status == 'F')
printf("%d\t%d\t%c\t%d\n", job[i].uid, job[i].remaining_time, job[i].status, job[i].weight);
return Temp;
}
void Show_After()
{
putchar('\n');
printf("结果详情\n");
printf("%s\t%s\t%s\t%s\t%s\t%s\n", "Name", "Arrival", "Finish", "Service", "cycling", "W=T/Ts");
for (int i = 0; i < TotalNum; i++)
{
jobInfo t = job[i];
int zz = t.finish_time - t.arrival_time;
printf("%d\t%d\t%d\t%d\t%d\t%.2lf\n", t.uid, t.arrival_time, t.finish_time, t.service_time, zz, 1.0 * zz / t.service_time);
}
putchar('\n');
printf("动态 高优先权优先调度算法 时间图\n\t|");
for (int i = 0; i <= CurrentTime; i++)
printf("%-4d", i);
putchar('\n');
for (int i = 0; i < TotalNum; i++)
{
printf("%d\t|", job[i].uid);
for (int j = 0; j < CurrentTime; j++)
{
if (job[i].uid == record[j])
printf("——");
else
printf(" ");
}
putchar('\n');
}
}
/*
1 0 5 3
2 1 4 3
3 2 1 5
4 4 2 4
5 5 1 5
1 0 5 3
2 1 3 5
3 2 1 3
4 3 1 4
5 4 2 2
1 0 6 3
2 1 4 3
3 2 1 4
4 4 1 5
5 8 3 4
*/
实验代码2
#include <stdio.h> // freopen函数所需头文件
#include <bits/stdc++.h>
using namespace std;
struct Job //动态高优先权优先调度算法
{
int pid;
int startTime; //到达时间
int readTime; //进入准备队列的时间
int runTime; //运行时间
int serverTime; //服务时间
int status; //0==等待,1==运行,2==完成
int weight; //优先权,值越大,优先权越高
int finishTime; //完成时间
} job[100001];
int n; //所有作业数量
int x; //每过一个时间单位权重的变化
bool operator<(const Job &a, const Job &b) //结构体重载函数
{
if (a.weight == b.weight)
return a.readTime > b.readTime; //优先权相等,更早进入准备队列的优先
return a.weight < b.weight;
}
bool cmp(Job a, Job b)
{
return a.startTime < b.startTime;
}
priority_queue<Job> ReadQueue; //进程准备队列
void updateStatus(int id, int time, int sta, int run, int we, int read) //修改进程状态
{
for (int i = 0; i < n; i++)
{
if (job[i].pid == id)
{
job[i].weight = we;
job[i].runTime = run;
job[i].finishTime = time;
job[i].status = sta;
job[i].readTime = read;
break;
}
}
}
int main()
{
// freopen("C:\\Users\\lwx\\Desktop\\data.txt", "r", stdin); //使用文件操作读取实验数据
freopen("data.txt", "r", stdin); //使用文件操作读取实验数据
printf("请输入进程数量:");
scanf("%d", &n);
printf("请依次输入进程ID、进程到达时间、进程服务时间、进程初始权重:\n");
for (int i = 0; i < n; i++)
{
scanf("%d %d %d %d", &job[i].pid, &job[i].startTime, &job[i].serverTime, &job[i].weight);
job[i].status = 0;
job[i].runTime = 0;
job[i].readTime = job[i].startTime;
}
printf("请输入每过一个时间单位权值的变化:");
scanf("%d", &x);
sort(job, job + n, cmp); //按到达时间排序
Job text; //临时队首的进程作业
int nowJob = 0; //已经进入准备队列的进程数量
for (int i = 0;; i++) //时间刻度,当nowJob==n && 准备队列为空时跳出循环
{
printf("\n第%d时刻:\n", i);
for (int j = nowJob; j < n; j++) //如果开始时刻等于当前时刻,将作业压入准备队列
if (job[j].startTime == i && job[i].status == 0)
ReadQueue.push(job[j]), nowJob++, text.readTime = i;
else
break;
if (!ReadQueue.empty()) //如果当前时刻准备队列不为空
{
text = ReadQueue.top(); //取出当前准备队列队首并移除
ReadQueue.pop();
//当前队首进行工作,工作一个时间单位之后,权值变化并重新进入准备队列
text.runTime++; //运行时间+1;
text.status = 1; //状态设为正在运行
printf("正在运行的进程ID:%d号进程\n", text.pid);
if (text.runTime == text.serverTime) //如果运行时间==服务时间,就结束
{
text.finishTime = i + 1; //完成时间
//这里完成时间为i+1,以0时刻开始,运行一个单位,那么在这里就结束了
//但是理论的结束时间应该为后一个时间单位,所以为i+1
text.status = 2; //状态设为2
text.readTime = i + 1;
updateStatus(text.pid, text.finishTime, text.status, text.runTime, text.weight, text.readTime);
continue; //下一时刻
}
else //如果没有结束
{
text.weight -= x; //权重减少
text.readTime = i + 1; //重新进入队列的时间
ReadQueue.push(text); //重新进入准备队列
updateStatus(text.pid, text.finishTime, text.status, text.runTime, text.weight, text.readTime); //修改进程状态
}
}
for (int j = 0; j < n; j++)
{
printf("id:%d\t上次进入队列时刻:%d\t状态:%d\t运行时间:%d\t当前权重%d\t结束时间:%d\n", job[j].pid, job[j].readTime, job[j].status, job[j].runTime, job[j].weight, job[j].finishTime);
}
if (ReadQueue.empty() && nowJob == n) //如果队列为空并且所有进程作业都已入队,说明完成
break; //跳出循环
}
printf("\n进程名\t到达时\t完成时\t周转时\t带权周转时\n");
double avg_sum_round_time = 0.0; //平均周转时间
double avg_sum_avg_time = 0.0; //平均带权周转时间
for (int i = 0; i < n; i++)
{
printf("%d\t%d\t%d\t%d\t%.2f\n", job[i].pid, job[i].startTime, job[i].finishTime, job[i].finishTime - job[i].startTime, 1.00 * (job[i].finishTime - job[i].startTime) / job[i].serverTime);
avg_sum_round_time += (job[i].finishTime - job[i].startTime);
avg_sum_avg_time += 1.00 * (job[i].finishTime - job[i].startTime) / job[i].serverTime;
}
printf("平均周转时间:%.2f\n", avg_sum_round_time / n);
printf("平均带权周转时间:%.2f\n", avg_sum_avg_time / n);
system("pause");
return 0;
}
/*
5
1 0 6 3
2 1 4 3
3 2 1 4
4 4 1 5
5 8 3 4
0
*/
四、实验结果
实验代码2
五、实验总结
动态高优先权优先调度算法的基本原理:
1.基本思想:为每个进程设置一个优先级,在调度的时候,选取优先级最大的任务去执行。
2.优先级可以是静态赋予:创建任务的时候,就指定优先级大小,并且保持不变,直到任务结束;也可以是动态赋予:优先级在执行任务中动态改变。
4.为了防止高优先级进程无休止地运行下去,程序可以在每个时钟中断时,降低当前进程的优先级。
5.在平时,可以把不同的任务划分为不同的优先级,然后在相同优先级的任务中使用时间片轮转。
本实验是模拟动态高优先权优先调度算法(抢占式)实现处理机调度的程序,实验中假设所有创建的进程都已在内存中,即一个等待运行的队列中,每次都将优先级最高的进程放到队首,然后每运行一次进程就降低一个单位的优先权再进行优先权比较;如果优先级相同则比较到达时间,将优先级最高且到达时间最早的进程放到队首。若出现优先权更高的进程,则立即停止当前正在执行的进程,并将处理机分配给新到达的优先权最高的进程。
动态优先数是指在进程创建时先确定一个初始优先数,以后在进程运行中随着进程特性的改变而不断修改优先数。这样,由于在开始时刻优先数很低而得不到CPU的进程,就能因为等待时间的增长而优先数变高从而得到CPU使用权,增加CPU利用率。动态调度,虽然有足够的灵活性变化的系统情况,但是却需要消耗更多的系统资源。
通过此次实验,我对动态高优先权优先调度算法有了更深的理解,知道了进程调度算法的实现过程和程序编写。对作业的完成时间、周转时间、带权周转时间的计算掌握地更加熟练。实验中,将理论和实践相结合,我对操作系统的学习更加深入。