一、实验目的
1、加深对进程概念的理解;
2、进一步掌握进程状态的转变、进程调度的策略及对系统性能的评价方法;
二、需求分析
2.1 程序设计任务
本程序的设计任务是模拟操作系统中的进程调度过程,具体实现先来先服务(FCFS)和短作业优先(SJF)调度算法。程序能够根据用户输入的进程信息,计算并输出各进程的完成时间、周转时间、带权周转时间,以及所有进程的平均周转时间和平均带权周转时间。
2.2 输入内容、形式及范围
- 进程个数 n(正整数,不超过1e4)
- 每个进程的到达时间:arrivalTime(非负整数)
- 每个进程的服务时间:serviceTime(正整数)
- 选择调度算法(1=FCFS,2=SJF)
2.3 输出形式
- 每个进程的开始时间
- 每个进程的输出:完成时间、周转时间、带权周转时间
- 总体:平均周转时间、平均带权周转时间
2.4 测试数据
2.4.1 正确的输入
2.4.2 错误的输入
- 进程数超过1e4
- 到达时间低于0
- 服务时间不为正整数
- 调度算法选择错误
三、概要设计
程序主要包括以下模块:
- Process结构体:存储进程相关信息,包括进程的ID,到达时间,服务时间,完成时间,周转时间,带权周转时间
- cmp函数:按到达时间对进程进行排序。
- FCFS函数:实现先来先服务调度。
- SJF函数:实现短作业优先调度。
- calculateAverages函数:计算平均周转时间和带权周转时间。
- main函数:程序入口,处理用户输入及调度算法选择。
流程如下:
- 用户输入进程数量及进程到达时间、服务时间。
- 根据用户选择的调度算法调用相应函数(FCFS或SJF)进行调度。
- 输出每个进程的信息及平均时间。
四、详细设计
4.1 FCFS 算法实现
- 初始化当前时间为0。
- 遍历进程:
- 更新当前时间为进程到达时间(如果当前时间小于到达时间)。
- 计算并记录完成时间、周转时间、带权周转时间。
- 输出每个时间点的调度状态。
4.2 SJF 算法实现
- 初始化当前时间和剩余进程数。
- 在每个时间点寻找到达且尚未完成的进程中,服务时间最短的进程。
- 处理等待状态,用时间累加,直到所有进程完成。
- 输出每个时间点的调度状态。
4.3 计算所有进程的平均时间
对所有进程的周转时间与带权周转时间进行累加,计算平均值。
五、调试分析
5.1 调试过程中遇到的问题及解决方案
问题:运行SJF时,未完成进程的初始化导致无法找到可调度进程。
解决方案:在进程结构体中增加finishTime初始值为-1来标识尚未完成。
5.2 算法的时空复杂度分析
5.2.1 算法时间复杂度分析
(1) FCFS算法
FCFS是一种简单且直观的非抢占式调度算法,其时间复杂度主要由以下两部分构成:
- 遍历进程列表: 每个进程在其到达时被调度执行。这一部分的时间复杂度为O(n),其中n为进程数量。
- 计算时间相关参数: 每个进程的完成时间、周转时间及带权周转时间均在一次遍历中完成,因此该部分复杂度为O(1)。
总体来说,FCFS在每次算法执行中的时间复杂度为O(n)。
(2) SJF算法
SJF算法在其非抢占式形态下同样是个复杂的调度策略:
- 查找当前可执行且服务时间最短的进程: 对于每一个调度决定,算法需遍历所有进程以确定当前可以调度的进程中服务时间最短者。这部分的时间复杂度为O(n)。
- 更新时间和状态: 此操作与FCFS类似,为O(1)。
因此,在最坏情况下,SJF算法的时间复杂度为O(n^2),因为每个进程可能依次被分析n次。
5.2.2 算法空间复杂度分析
对于这两种算法,空间复杂度主要由存储进程信息的数据结构决定:
- 进程数据结构: 需要额外的空间来存储处理器为每个进程维护的属性值(到达时间、服务时间等),该空间复杂度为O(n)。
- 辅助变量: 包括当前时间和剩余未调度进程数等,空间复杂度为O(1),不随输入规模变化。
总的来说,FCFS和SJF的空间复杂度都为O(n)。
5.2.3 改进设想
随着进程数量的增加,尤其在处理规模较大时,现有实现中的时间效率可能会不尽人意。针对这一点,以下几种改进方案可被考虑:
- 使用优先级队列: 对于SJF算法,将进程放入优先级队列可以直接获取当前服务时间最短的进程,将单次查找的时间复杂度从O(n)降低至O(log n)。
- 引入抢占性: 使用抢占式的SJF(又称为Shortest Remaining Time First, SRTF),能更为动态地适应处理等待时间较长的进程,减少可能的饿死现象。
- 并行处理: 在多核系统中,适用并行算法调度以提高效率,尤其是在大量进程需要同时计算属性值时。
- 动态调整时间片: 在需要兼顾实时性时,可以参考Round Robin算法,结合时间片动态调整来保证公平。
5.3 经验和体会
在实现过程中,理解进程状态转变对调度算法的关键影响。
对算法的效率与稳定性有更深刻的认识。
六、用户使用说明
使用程序步骤如下:
- 编译并运行程序。
- 按提示输入进程个数 n 及每个进程的到达时间和服务时间。
- 根据提示输入 1 选择FCFS算法或 2 选择SJF算法。
- 查看输出结果,进程调度状态以及相关统计信息。
七、测试结果
输入:
FCFS结果:
SJF结果:
总结:
算法 | 进程名 | A | B | C | D | E | 平均 |
到达时间 | 0 | 1 | 2 | 3 | 4 | ||
服务时间 | 4 | 3 | 5 | 2 | 4 | ||
FCFS | 完成时间 | 4 | 7 | 12 | 14 | 18 | |
周转时间 | 4 | 6 | 10 | 11 | 14 | 9 | |
带权周转时间 | 1 | 2 | 2 | 5.5 | 3.5 | 2.8 | |
SJF | 完成时间 | 4 | 9 | 18 | 6 | 13 | |
周转时间 | 4 | 8 | 16 | 3 | 9 | 8 | |
带权周转时间 | 1 | 2.67 | 3.2 | 1.5 | 2.25 | 2.12 |
代码
#include <bits/stdc++.h>
using namespace std;
struct Process {
int id;
int arrivalTime; // 到达时间
int serviceTime; // 服务时间
int finishTime; // 完成时间
int wholeTime; // 周转时间
double weightedWholeTime; // 带权周转时间
};
void FCFS(vector<Process>& processes) {
int currentTime = 0;// 当前时间初始化为0
for (auto& process : processes) {
if (currentTime < process.arrivalTime) {
currentTime = process.arrivalTime;// 当前时间小于进程到达时间时,更新当前时间
}
process.finishTime = currentTime + process.serviceTime;//计算
process.wholeTime = process.finishTime - process.arrivalTime;
process.weightedWholeTime = (double)process.wholeTime / process.serviceTime;
currentTime = process.finishTime;// 更新当前时间
cout << "时刻" << currentTime - process.serviceTime << ":进程" << process.id << "开始运行" << endl;
}
}
void SJF(vector<Process>& processes) {
int currentTime = 0;
int remaining = processes.size();// 剩余需要调度的进程数量初始化为所有进程
while (remaining > 0) {
int minIndex = -1;
int minServiceTime = INT_MAX;
for (int i = 0; i < processes.size(); ++i) {
// 找到当前时间前可执行且服务时间最短的进程
if (processes[i].finishTime == -1 && processes[i].arrivalTime <= currentTime && processes[i].serviceTime < minServiceTime) {
minServiceTime = processes[i].serviceTime;
minIndex = i;
}
}
if (minIndex == -1) {
currentTime++;
}
else {
processes[minIndex].finishTime = currentTime + processes[minIndex].serviceTime;
processes[minIndex].wholeTime = processes[minIndex].finishTime - processes[minIndex].arrivalTime;
processes[minIndex].weightedWholeTime = (double)processes[minIndex].wholeTime / processes[minIndex].serviceTime;
cout << "时刻" << currentTime << ":进程" << processes[minIndex].id << "开始运行" << endl;
currentTime = processes[minIndex].finishTime;//更新当前时间
remaining--;// 剩余未完成进程数量减少
}
}
}
// 计算平均周转时间和平均带权周转时间
void calculateAverages(const vector<Process>& processes, double& averageWT, double& averageWWT) {
int totalWT = 0;
double totalWWT = 0.0;
for (const auto& process : processes) {
totalWT += process.wholeTime;
totalWWT += process.weightedWholeTime;
}
averageWT = (double)totalWT / processes.size();
averageWWT = totalWWT / processes.size();
}
int main() {
int n;
cout << "请输入进程个数: ";
cin >> n;
vector<Process> processes(n);
if (n > 1e4) {
cout << "进程数量太多" << endl;
return 1;
}
cout << "请输入每个进程的到达时间和服务时间 (格式: 到达时间 空格 服务时间): " << endl;
for (int i = 0; i < n; i++) {
cout << "进程" << i + 1 << ": ";
processes[i].id = i + 1;
cin >> processes[i].arrivalTime >> processes[i].serviceTime;
processes[i].finishTime = -1; // -1表示进程尚未完成
if (processes[i].arrivalTime < 0) {
cout << "到达时间不能低于0!请重新输入" << endl;
i--;
}
else if (processes[i].serviceTime <= 0) {
cout << "服务时间应为正整数!请重新输入" << endl;
i--;
}
}
int choice;
cout << "选择调度算法 (1-FCFS, 2-SJF): ";
cin >> choice;
if (choice == 1) {
FCFS(processes);
}
else if (choice == 2) {
SJF(processes);
}
else {
cerr << "无效的选择!";
return 1;
}
double averageWT, averageWWT;
calculateAverages(processes, averageWT, averageWWT);
cout << "\n调度结果:" << endl;
for (const auto& process : processes) {
cout << "进程" << process.id << ":完成时间 = " << process.finishTime
<< ", 周转时间 = " << process.wholeTime
<< ", 带权周转时间 = " << process.weightedWholeTime << endl;
}
cout << "平均周转时间: " << averageWT << endl;
cout << "平均带权周转时间: " << averageWWT << endl;
return 0;
}