zoukankan      html  css  js  c++  java
  • 常用的调度算法

    调度算法是什么:

    根据系统的资源分配策略所规定的资源分配算法。对于不同的系统和系统目标,通常采用不同的调度算法。

    先来先服务(First Come First Served,FCFS)

    一种最简单的调度算法,在进程中采用FCFS算法时,则每次调度是从就绪队列中选择一个最先进入该队列的进程,为之分配处理机。

    最短作业优先(Shortest Job First,SJF)

    每次从就绪队列中选出一个估计运行时间最短的作业,为它分配处理机。
    SJF是改进版本的FCFS,在短作业优先前提下,短作业不必等待长作业,从而可以处理更多的短作业。

    优先权调度算法

    高响应比优先调度算法(Highest Response Ratio Next,HRRN)

    HRRN是对FCFS和SJF的一种综合改进,FCFS只考虑等待时间而未考虑执行时间,SJF方式只考虑执行时间而未考虑等待时间,HRRN同时考虑作业的等待时长和估计执行时长,从中选出响应比最高的作业执行。

    响应比R: (W+T)/T = 1+W/T
    T:作业估计执行时长
    W:作业等待时长

    基本流程:每当进行作业调度时,系统计算每个作业的响应比R,选择其中R最大者执行

    时间片轮转调度算法(RR,Round-Robin)

    RR是一种最古老、最简单、最公平且使用最广的算法,每个进程会被分配一个时间片(进程的执行时间)。

    基于FCFS算法,给予每个进程一个时间片,当一个进程的时间片用完时,将发生时钟中断,调度程序暂停运行进程,将进程送到就绪队列队尾,接着通过上下文执行就绪队列队首的进程。

    系统将所有的就绪进程按照先来先服务的原则排成一个队列,每次调度时,让CPU为队列的首进程服务一个时间片(几ms 到几百ms),当时间片到了时,由一个计时器发出时钟中断请求,调度程序就以此信号停止进程,并将停止了的进程送往就绪队列的末尾。

    如何确定进程的时间片:

    • 系统对进程响应时间的要求
    • 就绪队列进程的数量
    • 系统的性能

    多级反馈队列调度算法

    目前被公认的一种较好的进程调度算法,Unix系统采取的是这种调度算法,可以满足短进程和长进程的要求。

    • 设置多个就绪队列,从第一个队列到最后一个队列,优先权从高往低,时间片长度从成倍递增。

    • 当新进程进入内存后,首先放在第一队列的队尾,按FCFS原则等待调度,如果在分配的时间片中进程可以完成,则准备撤离系统,否则调度到第二队列的队尾,再同样按FCFS的原则等待调度执行,如此下去。

    • 当第1(i-1)队列均空时,才会调度第i队列中的进程运行。又有新的进程进入优先权较高的队列(第1(i-1)中的任何一个队列),则正在运行的进程放回到第i队列的队尾,此时处理机将为新进程服务。

    各类调度算法优缺点

    优点 缺点
    FCFS 利于长作业和CPU繁忙工作 不利于短作业和I/O繁忙工作
    SJF 有利于系统的吞吐量。 长作业的运行得不到保障
    HRRN 综合了FCFS和SJF算法的优点,兼顾了长进程和短进程的调度执行。 每次调度前需计算响应比,会给系统带来开销
    RR 简单易行,平均响应时间短 不利于紧急作业,时间片会影响系统性能

    代码实现:

    # -*- coding: utf-8 -*-
    """
    Created on Sun Nov  1 14:25:56 2020
    @author: 11099
    
    设数据是以表格的形式存在,包含的属性有进程名、到达时间、服务时间、开始时间、完成时间、周转时间、带权周转时间、平均值
    所有算法均遵从以下步骤:
    1.准备好数据(要求输入进程名、到达时间、服务时间,并准备好其他属性的变量)
    2.实现算法(该步骤生成开始时间、完成时间、周转时间、带权周转时间、平均值的具体数值)
    3.展示结果(都是两位有效数字的浮点数)
    """
    
    import pandas as pd
    # 定义调度算法的类
    class schedule_algorithm:
        # 初始化方法
        def __init__(self, name, arrived_time, served_time):
            self.data = pd.DataFrame({'到达时间': arrived_time, '服务时间': served_time}, dtype='float16', index=name, columns=['到达时间', '服务时间', '开始时间', '完成时间', '周转时间', '带权周转时间'])
            self.data.sort_values(['到达时间', '服务时间'], inplace=True)  # 先按到达时间从小到大排序,再按服务时间进行微调,意义在于同一到达时间可以先按服务时间进行调度
            self.data.iloc[0, 2], self.data.iloc[0, 3], self.data.iloc[0, 4], self.data.iloc[0, 5] = self.data.iloc[0, 0], self.data.iloc[0, 0]+self.data.iloc[0, 1], self.data.iloc[0, 1], 1.
        
        # 计算过程,单独写成一个方法方便算法的调用
        def cal(self, code):
            data, finish_time = self.data.copy(), self.data.iloc[0, 3].copy()
            for idx in data.index[1:]:
                ind = (data.loc[data['开始时间'].isnull(), '到达时间'] <= finish_time)
                if ind.any():
                    exec(code)
                    data.loc[self.ind, '开始时间'], 
                    data.loc[self.ind, '完成时间'], 
                    data.loc[self.ind, '周转时间'], 
                    data.loc[self.ind, '带权周转时间'], 
                    finish_time = finish_time, 
                    finish_time+data.loc[self.ind, '服务时间'], 
                    finish_time+data.loc[self.ind, '服务时间']-data.loc[self.ind, '到达时间'], 
                    (finish_time+data.loc[self.ind, '服务时间']-data.loc[self.ind, '到达时间'])/data.loc[self.ind, '服务时间'], finish_time+data.loc[self.ind, '服务时间']
                else:
                    data.loc[idx, '开始时间'], data.loc[idx, '完成时间'], data.loc[idx, '周转时间'], 
                    data.loc[idx, '带权周转时间'], finish_time = data.loc[idx, '到达时间'], 
                    data.loc[idx, '到达时间']+data.loc[idx, '服务时间'], 
                    data.loc[idx, '服务时间'], 1., 
                    data.loc[idx, '到达时间']+data.loc[idx, '服务时间']
            		data.loc['平均'] = data.mean()
            return data.applymap('{:.2f}'.format)
            
        # 先来先服务调度算法,基本思想是按进程或作业到达的前后顺序进行调度
        def FCFS(self):
            return self.cal('''self.ind = data.loc[ind[ind].index].index[0]''')  # 返回第一个值进行计算,先来先服务调度
        # 短作业优先是指对短作业或短进程优先调度的算法。设计目标是改进FCFS算法,减少作业或进程的平均周转时间
        def SJF(self):
            return self.cal('''self.ind = data.loc[ind[ind].index, '服务时间'].idxmin()''')  # 返回第一个最小值,若多个服务时间相同,则相当于按先来先服务调度
        # 响应比高者优先算法,基本思想是计算响应比(即1+等待时间/服务时间),选择响应比最高的进程运行
        def HRRN(self):
            return self.cal('''self.ind = (1+(finish_time-data.loc[ind[ind].index, '到达时间'])/data.loc[ind[ind].index, '服务时间']).idxmax()''')  # 返回第一个最大值,若多个响应比相同,则相当于按响应比中的先来先服务调度
        # 时间片轮转调度算法
        def RR(self):
            pass
        # 优先权调度算法
        def Priority(self):
            pass
        # 多级反馈队列调度算法
        def MFQ(self):
            pass
    
    # 只能在本文件中运行的代码块(即python的main文件名)
    if __name__ == '__main__':
        def input_data():
            name = input('请输入程序名(空格隔开):').split()
            arrived_time = input('请输入到达时间(空格隔开):').split()
            served_time = input('请输入服务时间(空格隔开):').split()
            try:
                s_a = schedule_algorithm(name, arrived_time, served_time)
                return s_a
            except:
                raise Exception('数据输入错误!')
        s_a = input_data()
        while True:
            print('
    请选择调度算法(1~6 or y,n):
    1.先来先服务
    2.短作业优先
    3.响应比高者优先
    4.时间片轮转
    5.优先权
    6.多级反馈队列
    y.重新输入数据
    n.退出')
            option = input('输入:')
            if option == 'n':
                break
            elif option == '1':
                fcfs = s_a.FCFS()
                print('对进程按先来先服务调度:
    {}
    {}'.format(fcfs, '-' * 30))
            elif option == '2':
                sjf = s_a.SJF()
                print('对进程按短作业优先调度:
    {}
    {}'.format(sjf, '-' * 30))
            elif option == '3':
                hrrn = s_a.HRRN()
                print('对进程按响应比高者优先调度:
    {}
    {}'.format(hrrn, '-' * 30))
            elif option == '4':
                print('对进程按时间片轮转调度:
    {}
    {}'.format(None, '-' * 30))
            elif option == '5':
                print('对进程按优先权调度:
    {}
    {}'.format(None, '-' * 30))
            elif option == '6':
                print('对进程按多级反馈队列调度:
    {}
    {}'.format(None, '-' * 30))
            elif option == 'y':
                s_a = input_data()
            else:
                print('命令输入错误!')
    
    
    package test;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.LinkedList;
    
    public class processMenu {
    
        ArrayList<JCB> jcb;// 存放所有进程
        LinkedList<JCB> link;// 存放已经进入队列的进程
        ArrayList<JCB> new_jcb;// 存放按指定调度算法排序后的进程
        JCB nowProess;// 当前应执行进程
    
        public void init() {//初始化
            jcb = new ArrayList<JCB>();
            link = new LinkedList<JCB>();
            new_jcb = new ArrayList<JCB>();
            JCB p1 = new JCB("1", 0, 4, 3);
            JCB p2 = new JCB("2", 1, 3, 2);
            JCB p3 = new JCB("3", 2, 5, 3);
            JCB p4 = new JCB("4", 3, 2, 1);
            JCB p5 = new JCB("5", 4, 4, 5);
            jcb.add(p1);
            jcb.add(p2);
            jcb.add(p3);
            jcb.add(p4);
            jcb.add(p5);
            //先将jcb排序,便于下面的算法实现,就不需要再定义一个标识进程是否已到达的boolean,即无需每次都从头开始扫描jcb容器,
            //而是用一个K记录下当前已经扫描到的位置,一次遍历即可,提高了算法效率。
            Collections.sort(jcb, new compareAt_St());
        }
    
        public void FCFS() {//先来先服务算法
            ProcessQueue pq = new ProcessQueue();//调用内部类
            pq.EnqueueLast();//让最先到达的进程先入队
            System.out.println("*****************************************************");
            while (!link.isEmpty()) {//while(new_jcb.size()!=jcb.size())
                pq.DisplayQueue();//打印当前队列中的进程
                pq.Dequeue();//出队,一次一个
                pq.EnqueueLast();//已到达的进程入队
            }
        }
    
        public void SJF() {// 短作业优先算法
            ProcessQueue pq = new ProcessQueue();
            pq.EnqueueLast();
            System.out.println("*****************************************************");
            while (!link.isEmpty()) {
                pq.DisplayQueue();//打印当前队列中的进程
                pq.Dequeue();//出队,一次一个
                pq.EnqueueLast();//已到达的进程入队
                Collections.sort(link, new compareSt());//队列中的进程还需按服务时间长度进行排序
            }
        }
    
        public void RR() {//时间片轮转调度算法
            ProcessQueue pq = new ProcessQueue();
            pq.EnqueueLast();
            System.out.println("*****************************************************");
            while (!link.isEmpty()) {
                pq.DisplayQueue();//打印当前队列中的进程
                pq.Dequeue(1);//出队,一次一个,因为上一轮出的得让刚到达的进程先进队列,所以没办法,进队操作只能也放在这个函数里了
            }
        }
    
        public void HRN() {//最高响应比优先调度算法
            ProcessQueue pq = new ProcessQueue();
            pq.EnqueueLast();
            System.out.println("*****************************************************");
            while (!link.isEmpty()) {
                pq.DisplayQueue();//打印当前队列中的进程
                pq.Dequeue();//出队,一次一个
                pq.EnqueueLast();//已到达的进程入队
                Collections.sort(link, new comparePriority());//队列中的进程还需按响应比进行排序
            }
        }
    
        class ProcessQueue {
            int k = 0;// jcb中的进程遍历时的下标
            int nowTime = 0;// 当前时间
            double sliceTime;//轮转调度时间片
            int i = 0;//记录当前出入队列的次数
    
            public void EnqueueLast() {//进程首次入队,可一次进多个,从队尾进入
                while (k < jcb.size()) {//当遍历完jcb中的所有进程时结束
                    if (jcb.get(k).arriveTime <= nowTime) {//已经到达的进程按到达时间先后进入队列
                        link.addLast(jcb.get(k));
                        k++;
                    } else {
                        break;//如果该进程还未入队,即先结束遍历,保留当前下标k值,注意:此处不要k--;
                    }
                }
            }
    
            public void EnqueueFirst() {//进程首次入队,可一次进多个,从队首进入
                while (k < jcb.size()) {//当遍历完jcb中的所有进程时结束
                    if (jcb.get(k).arriveTime <= nowTime) {//已经到达的进程按到达时间先后进入队列
                        link.addFirst(jcb.get(k));
                        k++;
                    } else {
                        break;//如果该进程还未入队,即先结束遍历,保留当前下标k值,注意:此处不要k--;
                    }
                }
            }
    
            public void Dequeue() {//进程出队,一次只出一个
                nowProess = link.removeFirst();//移除队列的队首元素并且返回该对象元素
                nowProess.beginTime = nowTime;//计算开始时间,即为上一个进程的结束时间
                nowProess.finshTime = nowProess.beginTime + nowProess.serveTime;//计算结束时间,该进程开始时间+服务时间
                nowProess.roundTime = nowProess.finshTime - nowProess.arriveTime;//计算周转时间
                nowProess.aveRoundTime = (double) nowProess.roundTime / nowProess.serveTime;//计算平均周转时间
                nowTime = nowProess.finshTime;//获得结束时间,即当前时间,方便判断剩下的进程是否已到达
                new_jcb.add(nowProess);//经处理过数据后加入new_jcb容器
                for (int i = 0; i < link.size(); ++i) {
                    link.get(i).waitTime++;//所有进入等待队列的进程等待时间+1,此处只为最高响应比算法所用
                }
            }
    
            public void Dequeue(double sliceTime) {//重载Dequeue方法,实现轮转调度算法的出队
                nowProess = link.removeFirst();//移除队列的队首元素并且返回该对象元素
                if (nowProess.firstTimeTag == false) {
                    /*轮转调度进程可能会多次反复进出队列,不像FCFS和SJF的进程只会进出一次,所以计算开始时间可以设个标志位,让每个进程在
    				 * 第一次执行时记录一遍即可*/
                    nowProess.beginTime = nowTime;//进程开始执行的时间
                    nowProess.firstTimeTag = true;//计算第一次即可,下次无需更新计算
                }
                nowTime += sliceTime;//每次出队,用时一个时间片,更新当前时间
                nowProess.clock += sliceTime;//更新当前出队列的进程已服务时间
                if (nowProess.clock >= nowProess.serveTime) {
                    nowProess.finshTime = nowTime;//计算该进程完成时间
                    nowProess.roundTime = nowProess.finshTime - nowProess.arriveTime;//计算周转时间
                    nowProess.aveRoundTime = (double) nowProess.roundTime / nowProess.serveTime;//计算平均周转时间
                    new_jcb.add(nowProess);//经处理过数据后加入new_jcb容器
                    EnqueueFirst();//已到达的进程先入队
                } else {
                    EnqueueFirst();//已到达的进程先入队
                    link.addLast(nowProess);//上一轮出的再紧接着进入队尾
                }
            }
    
            public void DisplayQueue() {//队列中等候的进程
                i++;
                System.out.println("第" + i + "次队列中排队的进程:" + link);
            }
        }
    
        public void printProcess() {
            System.out.println("进程名 到达时间  服务时间   开始时间  完成时间  周转时间  带权周转时间");
            for (int i = 0; i < new_jcb.size(); ++i) {
                System.out.println(" "+"P" + new_jcb.get(i).name + "        " + new_jcb.get(i).arriveTime + "       " +
                        new_jcb.get(i).serveTime + "         " + new_jcb.get(i).beginTime + "         " + new_jcb.get(i).finshTime +
                        "         " + new_jcb.get(i).roundTime + "        " + new_jcb.get(i).aveRoundTime);
            }
            new_jcb.clear();//清空new_jcb容器内的内容,方便存储各种算法的结果并展示
        }
    }
    
    class compareSt implements Comparator<JCB> {// 按服务时间升序
    
        public int compare(JCB arg0, JCB arg1) {
            return arg0.serveTime - arg1.serveTime;
        }
    }
    
    class compareAt_St implements Comparator<JCB> {// 按到达时间升序,若到达时间相同,按服务时间升序
    
        public int compare(JCB o1, JCB o2) {
            int a = o1.arriveTime - o2.arriveTime;
            if (a > 0)
                return 1;
            else if (a == 0) {
                return o1.serveTime > o2.serveTime ? 1 : -1;
            } else
                return -1;
        }
    }
    
    class comparePriority implements Comparator<JCB> {//按响应比升序排序
    
        public int compare(JCB o1, JCB o2) {
            double r1 = (double) o1.waitTime / o1.serveTime;
            double r2 = (double) o2.waitTime / o2.serveTime;
            return r1 > r2 ? 1 : -1;
        }
    
    }
    

    运行结果:

    输入数据,并选择调度算法

    先来先服务(FCFS)


    短作业(SJF)


    时间片轮转(RR)

    高响应优先(HRRN)


    总结

    此次调度算法由我们小组成员共同完成,使用Java与Python语言模拟。
    组内分工如下

    姓名 负责
    廖涛 博客园文档撰写
    李威剑 使用Python编码实现
    邓强 测试用例(Python)
    周颖 测试用例(Java)
    苏智勇 使用Java编码实现

    参考:

    https://blog.csdn.net/qq_36143023/article/details/80623662

  • 相关阅读:
    正文提取2
    使用mysqlimport导入数据
    batch_get_real_image_urls.py 博源
    用于网页分类的python工具包
    python learning base 不错
    20101010网站更新部署
    20101010网站更新部署
    正文提取
    python property
    jquery slideUp()
  • 原文地址:https://www.cnblogs.com/TheFaceOfAutumnWhenSummerEnd/p/13928391.html
Copyright © 2011-2022 走看看