操作系统-先来先服务FCFS和短作业优先SJF进程调度算法-详细设计附源码

1,798 阅读12分钟

一、需求分析

1、程序设计的任务和目的

通过这次实验,加深对进程概念的理解,进一步掌握进程状态的转变、进程调度的策略及对系统性能的评价方法。

2、输入的形式和输入值的范围

进程个数n;每个进程的到达时间T1, … ,Tn和服务时间S1, … ,Sn;选择算法1-FCFS,2-SJF。

3、输出的形式

要求模拟整个调度过程,输出每个时刻的进程运行状态,如“时刻3:进程B开始运行”等等;要求输出计算出来的每个进程的周转时间、带权周转时间、所有进程的平均周转时间以及带权平均周转时间。 实现提示:

4、程序所能达到的功能

设计程序模拟进程的先来先服务FCFS和短作业优先SJF调度过程。假设有n个进程分别在T1, … ,Tn时刻到达系统,它们需要的服务时间分别为S1, … ,Sn。分别采用先来先服务FCFS和短作业优先SJF进程调度算法进行调度,计算每个进程的完成时间、周转时间和带权周转时间,并且统计n个进程的平均周转时间和平均带权周转时间。

5、测试数据,包括正确的输入及其输出结果和含有错误的输入及其输出结果

正确用例

输入

请输入进程数量:5
请输入进程1的到达时间ArrivalTime[1]:0
请输入进程1的服务时间ServiceTime[1]:4
请输入进程2的到达时间ArrivalTime[2]:1
请输入进程2的服务时间ServiceTime[2]:3
请输入进程3的到达时间ArrivalTime[3]:2
请输入进程3的服务时间ServiceTime[3]:5
请输入进程4的到达时间ArrivalTime[4]:3
请输入进程4的服务时间ServiceTime[4]:2
请输入进程5的到达时间ArrivalTime[5]:4
请输入进程5的服务时间ServiceTime[5]:4

请选择想要先使用的算法(1-FCFS,2-SJF):1

输出

您选择的是1-FCFS算法
时刻0进程1开始运行
时刻5进程2开始运行
时刻8进程3开始运行
时刻13进程4开始运行
时刻15进程4开始运行

周转信息如下表:
                Process 1 Process 2 Process 3 Process 4 Process 5
ArrivalTime             0         1         2         3         4
ServiceTime             4         3         5         2         4
FinishTime              4         7        12        14        18
WholeTime               4         6        10        11        14
WeightWholeTime      1.00      2.00      2.00      5.50      3.50
平均周转时间:      9.00
平均带权周转时间:      2.80

错误用例(输入负值服务事件引起程序错误)

输入

请输入进程数量:3
请输入进程1的到达时间ArrivalTime[1]:1
请输入进程1的服务时间ServiceTime[1]:-5
请输入进程2的到达时间ArrivalTime[2]:2
请输入进程2的服务时间ServiceTime[2]:-4
请输入进程3的到达时间ArrivalTime[3]:3
请输入进程3的服务时间ServiceTime[3]:-3

请选择想要先使用的算法(1-FCFS,2-SJF):1

输出

您选择的是1-FCFS算法

Process finished with exit code 0

二、概要设计

1、抽象数据类型的定义

int n;//进程数
int ArrivalTime[MaxNum];//进程到达时间T[i]
int ServiceTime[MaxNum];//进程服务时间S[i]
int FinishTime[MaxNum];//完成时间
int WholeTime[MaxNum];//周转时间
double WeightWholeTime[MaxNum];//带权周转时间
double AverageWT = 0;//平均周转时间
double AverageWWT = 0;//平均带权周转时间
int isAlgorithm;//进程算法输入值,用于验证输入,1为FCFS,2为SJF

2、主程序的流程

int main() {
    //创建TimeCal类
    TimeCal timeCal{};

    //输入进程信息
    //将输入的ArrivalTime信息与ServiceTime信息存储进数组
    timeCal.InputProcess();

    //获取算法选择输入
    timeCal.InputAlgorithm();

    if (timeCal.isAlgorithm == 1)    //调用FCFS算法进行调度计算
    {
        timeCal.AlgorithmFCFS();
    } else//调用SJF算法进行调度计算
    {
        timeCal.AlgorithmSJF();
    }

    //输出调度过程
    timeCal.PrintSchedule();

    //输出周转时间、带权周转时间、平均周转时间及带权平均周转时间
    timeCal.Print();

    return 0;
}

3、各程序模块之间的层次(调用)关系

int main() {
    ···
    //输入进程信息
    //将输入的ArrivalTime信息与ServiceTime信息存储进数组
    timeCal.InputProcess();

    //获取算法选择输入
    timeCal.InputAlgorithm();

    if (timeCal.isAlgorithm == 1)    //调用FCFS算法进行调度计算
    {
        timeCal.AlgorithmFCFS();
    } else//调用SJF算法进行调度计算
    {
        timeCal.AlgorithmSJF();
    }

    //输出调度过程
    timeCal.PrintSchedule();

    //输出周转时间、带权周转时间、平均周转时间及带权平均周转时间
    timeCal.Print();

    return 0;
}

//输入进程信息
//将输入的ArrivalTime信息与ServiceTime信息存储进数组
void InputProcess() {
    ···
}

//获取算法选择输入
void InputAlgorithm() {
    ···
    IsAlgorithm();
}

//算法存储
void IsAlgorithm() {
    ···
    InputAlgorithm();
}

//调用FCFS算法进行调度计算
void AlgorithmFCFS() {
	//计算周转信息及调度情况
    ···
}

//调用SJF算法进行调度计算
void AlgorithmSJF() {
	//计算周转信息及调度情况
    ···
}

//输出调度过程
void PrintSchedule() {
    ···
}

//输出周转时间、带权周转时间、平均周转时间及带权平均周转时间
void Print() {
    ···
}

三、详细设计

实现程序模块的具体算法

1、FCFS算法

//调用FCFS算法进行调度计算
void AlgorithmFCFS() {

    //初始化FinishTime[0]
    FinishTime[0] = 0;

    for (int i = 1; i <= n; i++) {
        //计算完成时间
        FinishTime[i] = FinishTime[i - 1] + ServiceTime[i];

        //计算周转时间
        WholeTime[i] = FinishTime[i] - ArrivalTime[i];

        //计算带权周转时间
        WeightWholeTime[i] = (double) WholeTime[i] / ServiceTime[i];

        //计算平均周转时间
        AverageWT += WholeTime[i];

        //计算平局带权周转时间
        AverageWWT += WeightWholeTime[i];
    }
    AverageWT = (double) AverageWT / n;
    AverageWWT = (double) AverageWWT / n;
}

2、SJF算法

//调用SJF算法进行调度计算
void AlgorithmSJF() {

	//初始化数据
    ···

    //从小到大冒泡排序QueueServiceTime
    ···

    //计算完成时间
    int temp = -1, old_process = 0, new_process = 0;//定义暂存值、上一个进程、本进程

    for (int i = 1; i <= n; i++) {

        old_process = new_process;

        //查找服务时间最短的进程
        if (i != 1) {
    		···
        } else//若为第1个进程,直接进入队列,完成时间即为服务时间
        {
    		···
        }

        //查找该最短服务时间对应的进程号
    	···

        //计算存储完成时间
        if (i != 1) {
            FinishTime[new_process] = FinishTime[old_process] + temp;
        } else {
            FinishTime[i] = ArrivalTime[i] + ServiceTime[i];
        }

    }

	//计算周转信息及调度情况,同FCFS算法
    ···
}

四、调试分析

调试过程中遇到的问题以及解决方法,设计与实现的回顾讨论和分析

问题 描述 解决方法
输出杂乱 数据较多,输出情况复杂,数据眼花缭乱 使用setw方法及setprecision方法规格化表格输出

算法的性能分析(包括基本操作和其它算法的时间复杂度和空间复杂度的分析)及其改进设想

性能分析

算法 时间复杂度 空间复杂度
FCFS算法 T(n) = O(n) S(n) = O(n)
SJF算法 T(n) = O(n2) S(n) = O(n)
PrintSchedule输出调度过程 T(n) = O(n) S(n) = O(n)
Print数据输出 T(n) = O(n) S(n) = O(n)

改进设想

加入多次算法选择功能

五、用户使用说明

使用说明

  • 控制台会提示要求用户进行输入,按提示输入内容即可
  • 不可输入负值服务时间,会造成错误
  • 选择算法输入完成后将会输出调度过程及周转信息

六、测试结果

测试结果,包括输入和输出

D:\Documents\MyCourse\OperatingSystem\cmake-build-debug\chapter01.exe
请输入进程数量:5
请输入进程1的到达时间ArrivalTime[1]:0
请输入进程1的服务时间ServiceTime[1]:4
请输入进程2的到达时间ArrivalTime[2]:1
请输入进程2的服务时间ServiceTime[2]:3
请输入进程3的到达时间ArrivalTime[3]:2
请输入进程3的服务时间ServiceTime[3]:5
请输入进程4的到达时间ArrivalTime[4]:3
请输入进程4的服务时间ServiceTime[4]:2
请输入进程5的到达时间ArrivalTime[5]:4
请输入进程5的服务时间ServiceTime[5]:4

请选择想要先使用的算法(1-FCFS,2-SJF):1

您选择的是1-FCFS算法
时刻0进程1开始运行
时刻5进程2开始运行
时刻8进程3开始运行
时刻13进程4开始运行
时刻15进程4开始运行

周转信息如下表:
                Process 1 Process 2 Process 3 Process 4 Process 5
ArrivalTime             0         1         2         3         4
ServiceTime             4         3         5         2         4
FinishTime              4         7        12        14        18
WholeTime               4         6        10        11        14
WeightWholeTime      1.00      2.00      2.00      5.50      3.50
平均周转时间:      9.00
平均带权周转时间:      2.80

Process finished with exit code 0

七、附录

带注释的源程序

#include <iostream>
#include <iomanip>

using namespace std;
#define MaxNum 100

class TimeCal {
public:

    int n;//进程数
    int ArrivalTime[MaxNum];//进程到达时间T[i]
    int ServiceTime[MaxNum];//进程服务时间S[i]
    int FinishTime[MaxNum];//完成时间
    int WholeTime[MaxNum];//周转时间
    double WeightWholeTime[MaxNum];//带权周转时间
    double AverageWT = 0;//平均周转时间
    double AverageWWT = 0;//平均带权周转时间
    int isAlgorithm;//进程算法输入值,用于验证输入,1为FCFS,2为SJF

    //输入进程信息
    //将输入的ArrivalTime信息与ServiceTime信息存储进数组
    void InputProcess() {
        cout << "请输入进程数量:";
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cout << "请输入进程" << i << "的到达时间ArrivalTime[" << i << "]:";
            cin >> ArrivalTime[i];
            cout << "请输入进程" << i << "的服务时间ServiceTime[" << i << "]:";
            cin >> ServiceTime[i];
        }
    }

    //获取算法选择输入
    void InputAlgorithm() {
        cout << endl << "请选择想要先使用的算法(1-FCFS,2-SJF):";
        cin >> isAlgorithm;
        IsAlgorithm();
    }

    //算法存储
    void IsAlgorithm() {
        if (isAlgorithm == 1) {
            cout << endl << "您选择的是1-FCFS算法" << endl;
        } else if (isAlgorithm == 2) {
            cout << endl << "您选择的是2-SJF算法" << endl;
        } else {
            cout << "算法值" << isAlgorithm << "有误,请重新输入正确的算法类型(1-FCFS,2-SJF)" << endl;
            InputAlgorithm();
        }
    }

    //调用FCFS算法进行调度计算
    void AlgorithmFCFS() {

        //初始化FinishTime[0]
        FinishTime[0] = 0;

        for (int i = 1; i <= n; i++) {
            //计算完成时间
            FinishTime[i] = FinishTime[i - 1] + ServiceTime[i];

            //计算周转时间
            WholeTime[i] = FinishTime[i] - ArrivalTime[i];

            //计算带权周转时间
            WeightWholeTime[i] = (double) WholeTime[i] / ServiceTime[i];

            //计算平均周转时间
            AverageWT += WholeTime[i];

            //计算平局带权周转时间
            AverageWWT += WeightWholeTime[i];
        }
        AverageWT = (double) AverageWT / n;
        AverageWWT = (double) AverageWWT / n;
    }

    //调用SJF算法进行调度计算
    void AlgorithmSJF() {

        int QueueServiceTime[MaxNum];//排序服务时间
        int CopyServiceTime[MaxNum];//备份服务时间

        //初始化QueueServiceTime
        for (int i = 1; i <= n; i++) {
            QueueServiceTime[i] = ServiceTime[i];
        }

        //初始化CopyServiceTime
        for (int i = 1; i <= n; i++) {
            CopyServiceTime[i] = ServiceTime[i];
        }

        //给FinishTime[0]附初值
        FinishTime[0] = 0;

        //从小到大冒泡排序QueueServiceTime
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n - i; j++) {
                if (QueueServiceTime[j] > QueueServiceTime[j + 1]) {
                    int temp = QueueServiceTime[j];
                    QueueServiceTime[j] = QueueServiceTime[j + 1];
                    QueueServiceTime[j + 1] = temp;
                }
            }
        }

        //计算完成时间
        int temp = -1, old_process = 0, new_process = 0;//定义暂存值、上一个进程、本进程

        for (int i = 1; i <= n; i++) {

            old_process = new_process;

            //查找服务时间最短的进程
            if (i != 1) {
                for (int j = 1; j <= n; j++) {
                    if (QueueServiceTime[j] > 0 && ArrivalTime[j] <= FinishTime[old_process] + 1) {
                        temp = QueueServiceTime[j];
                        QueueServiceTime[j] = -1;
                        break;
                    }
                }
            } else//若为第1个进程,直接进入队列,完成时间即为服务时间
            {
                for (int j = 1; j <= n; j++) {
                    if (QueueServiceTime[j] == ServiceTime[i]) {
                        temp = QueueServiceTime[j];
                        QueueServiceTime[j] = -1;
                        break;
                    }
                }
            }

            //查找该最短服务时间对应的进程号
            for (int j = 1; j <= n; j++) {
                if (CopyServiceTime[j] == temp && CopyServiceTime[j] > 0) {
                    new_process = j;
                    CopyServiceTime[j] = -1;
                    break;
                }
            }

            //计算存储完成时间
            if (i != 1) {
                FinishTime[new_process] = FinishTime[old_process] + temp;
            } else {
                FinishTime[i] = ArrivalTime[i] + ServiceTime[i];
            }

        }

        for (int i = 1; i <= n; i++) {
            //计算周转时间
            WholeTime[i] = FinishTime[i] - ArrivalTime[i];

            //计算带权周转时间
            WeightWholeTime[i] = (double) WholeTime[i] / ServiceTime[i];

            //计算平均周转时间
            AverageWT += WholeTime[i];

            //计算平局带权周转时间
            AverageWWT += WeightWholeTime[i];
        }
        cout << endl;

        AverageWT = (double) AverageWT / n;
        AverageWWT = (double) AverageWWT / n;
    }

    //输出调度过程
    void PrintSchedule() {
        int temp = 0;
        int CopyFinishTime[MaxNum];//备份服务时间

        //初始化CopyServiceTime
        for (int i = 1; i <= n; i++) {
            CopyFinishTime[i] = FinishTime[i];
        }

        if (FinishTime[1] >= 0)//判断是否已有数据输入
            //根据完成时间排序输出
            for (int i = 1; i <= n; i++) {

                if (i != 1) {
                    for (int j = 1; j <= n; j++) {
                        if (temp != 0) {
                            if (CopyFinishTime[j] > 0) {
                                temp = (CopyFinishTime[temp] < CopyFinishTime[j] ? temp : j);
                            }
                        } else {
                            if (CopyFinishTime[j] > 0) {
                                temp = j;
                            }
                        }
                    }
                    if (i != 2) { cout << "进程" << temp << "开始运行" << endl; }
                    cout << "时刻" << FinishTime[temp] + 1;
                    if (i == n) {
                        for (int j = 1; j < n; j++) {
                            if (CopyFinishTime[j] > 0) {
                                temp = j;
                            }
                        }
                        cout << "进程" << temp << "开始运行" << endl << endl;
                    }
                    CopyFinishTime[temp] = -1;
                    temp = 0;
                } else {
                    cout << "时刻0进程" << i << "开始运行" << endl;
                }
            }
    }

    //输出周转时间、带权周转时间、平均周转时间及带权平均周转时间
    void Print() {
        if (FinishTime[1] >= 0)//判断是否已有数据输入
        {
            cout << left << setw(15) << "周转信息如下表:" << endl;

            cout << left << setw(15) << "";
            for (int i = 1; i <= n; i++) {
                cout << right << setw(8) << "Process" << setw(2) << i;
            }
            cout << endl;

            cout << left << setw(15) << "ArrivalTime";
            for (int i = 1; i <= n; i++) {
                cout << right << setw(10) << ArrivalTime[i];
            }
            cout << endl;

            cout << left << setw(15) << "ServiceTime";
            for (int i = 1; i <= n; i++) {
                cout << right << setw(10) << ServiceTime[i];
            }
            cout << endl;

            cout << left << setw(15) << "FinishTime";
            for (int i = 1; i <= n; i++) {
                cout << right << setw(10) << FinishTime[i];
            }
            cout << endl;

            cout << left << setw(15) << "WholeTime";
            for (int i = 1; i <= n; i++) {
                cout << right << setw(10) << WholeTime[i];
            }
            cout << endl;

            cout << left << setw(15) << "WeightWholeTime";
            for (int i = 1; i <= n; i++) {
                cout << right << setw(10) << fixed << setprecision(2) << WeightWholeTime[i];
            }
            cout << endl;

            cout << "平均周转时间:" << setw(10) << setprecision(2) << AverageWT << endl;
            cout << "平均带权周转时间:" << setw(10) << setprecision(2) << AverageWWT << endl;
        }
    }
};


int main() {
    //创建TimeCal类
    TimeCal timeCal{};

    //输入进程信息
    //将输入的ArrivalTime信息与ServiceTime信息存储进数组
    timeCal.InputProcess();

    //获取算法选择输入
    timeCal.InputAlgorithm();

    if (timeCal.isAlgorithm == 1)    //调用FCFS算法进行调度计算
    {
        timeCal.AlgorithmFCFS();
    } else//调用SJF算法进行调度计算
    {
        timeCal.AlgorithmSJF();
    }

    //输出调度过程
    timeCal.PrintSchedule();

    //输出周转时间、带权周转时间、平均周转时间及带权平均周转时间
    timeCal.Print();

    return 0;
}