【操作系统】FCFS、SJF、HRRN、RR、EDF、LLF调度算法及python实现代码

文章目录

一、先来先服务调度算法(FCFS)

二、短作业优先调度算法(SJF)

三、高响应比优先调度算法(HRRN)

四、轮转调度算法(RR)

五、最早截至时间优先算法(EDF)

六、最低松弛度优先算法(LLF)

相关时间计算

周转时间 = 作业完成时刻 - 作业到达时刻

等待时间 = 周转时间 - 运行时间
带权周转时间 = 周转时间 / 服务时间

平均周转时间 = 作业周转时间之和 / 作业个数

平均带权周转时间 = 带权周转时间之和 / 作业个数

服务时间:作业的运行时间

一、先来先服务调度算法(FCFS)

如果早就绪的进程排在就绪队列的前面,迟就绪的进程排在就绪队列的后面,那么先来先服务(FCFS: first come first service)总是把当前处于就绪队列之首的那个进程调度到运行状态。也就说,它只考虑进程进入就绪队列的先后,而不考虑它的下一个CPU周期的长短及其他因素。

先来先服务的调度算法是最简单的调度算法,既可以用于作业调度 ,也可以用于程序调度,当作业调度中采用该算法时,系统将按照作业到达的先后次序来进行调度,优先从后备队列中,选择一个或多个位于队列头部的作业,把他们调入内存,分配所需资源、创建进程,然后放入"就绪队列",直到该进程运行到完成或发生某事件堵塞后,进程调度程序才将处理机分配给其他进程。

实现代码:

python 复制代码
avg_turnaround_time = 0.0
aqtt = 0.0

class Progress:
    def __init__(self):
        self.pro_name = ""
        self.arrive_time = 0
        self.service_time = 0
        self.finish_time = 0
        self.cycling_time = 0
        self.float_wi = 0.0

courses = [Progress() for _ in range(80)]

def health_examine(course_num):
    return

def progress_num(course_num):
    i = 0
    for i in range(course_num):
        print("Enter information for process %d" % (i + 1))
        courses[i].pro_name = input("Enter process name: ") #进程名
        courses[i].arrive_time = int(input("Enter arrival time: ")) #到达时间
        courses[i].service_time = int(input("Enter service time: ")) #服务时间
    return

def finish_time(course_num):
    i = 0
    if courses[0].arrive_time != 0:
        courses[0].finish_time = courses[0].arrive_time + courses[0].service_time
    courses[0].finish_time = courses[0].service_time
    for i in range(1, course_num):
        if courses[i - 1].finish_time >= courses[i].arrive_time:
            courses[i].finish_time = courses[i - 1].finish_time + courses[i].service_time
        else:
            courses[i].finish_time = courses[i].arrive_time + courses[i].service_time
    return

def cycling_time(course_num): #周转时间
    for i in range(course_num):
        courses[i].cycling_time = courses[i].finish_time - courses[i].arrive_time
    return

def float_wi(course_num): #带权周转时间
    for i in range(course_num):
        courses[i].float_wi = float(courses[i].cycling_time) / courses[i].service_time
    return

def avg_turnaround_time(course_num): #平均周转时间
    sum_turnaround_time = 0
    for i in range(course_num):
        sum_turnaround_time += courses[i].cycling_time
    global avg_turnaround_time
    avg_turnaround_time = sum_turnaround_time / course_num
    return

def aqtt(course_num): #平均带权周转时间
    sum_float_wi = 0
    for i in range(course_num):
        sum_float_wi += courses[i].float_wi
    global aqtt
    aqtt = sum_float_wi / course_num
    return

def print_fifo(course_num):
    print("Process information:")
    print("Process\tArrival Time\tService Time\tFinish Time\tTurnaround Time\tWeighted Turnaround")
    for i in range(course_num):
        print("%s\t%d\t%d\t%d\t%d\t%.2f" % (courses[i].pro_name, courses[i].arrive_time, courses[i].service_time, courses[i].finish_time, courses[i].cycling_time, courses[i].float_wi))
    print("Average Turnaround Time\tAverage Weighted Turnaround Time")
    print("%.2f\t%.2f" % (avg_turnaround_time, aqtt))
    return

def main():
    course_num = 5
    course_num = int(input("Enter the number of processes: "))
    progress_num(course_num)
    finish_time(course_num)
    cycling_time(course_num)
    float_wi(course_num)
    avg_turnaround_time(course_num)
    aqtt(course_num)
    print_fifo(course_num)

if __name__ == "__main__":
    main()

输出信息:

Enter the number of processes: 3
Enter information for process 1
Enter process name: p1
Enter arrival time: 0
Enter service time: 5
Enter information for process 2
Enter process name: p2
Enter arrival time: 2
Enter service time: 1
Enter information for process 3
Enter process name: p3
Enter arrival time: 3
Enter service time: 4
Process information:
Process	Arrival Time	Service Time	Finish Time	Turnaround Time	Weighted Turnaround
p1	0	5	5	5	1.00
p2	2	1	6	4	4.00
p3	3	4	10	7	1.75
Average Turnaround Time	Average Weighted Turnaround Time
5.33	2.25

进程已结束,退出代码0

二、短作业优先调度算法(SJF)

算法思想是追求更少的平均时间,最少的平均周转时间,最少的平均平均带权周转时间算法规则是最短的作业/进程优先得到服务(所谓"最短",是指要求服务时间最短)。即可用于作业调度,也可用于进程调度。用于进程调度时称为"短进程优先(SPF,Shortest Process First)算法"。 SJF和SPF都是非抢占式的算法。但是也有抢占式的版本----最短剩余时间优先算法。(SRTN,Shortest Remaining Time Next)

  • 优点: "最短的"平均等待时间、平均周转时间
  • 缺点: 不公平。对短作业有利,对长作业不利。可能产生==饥饿现象。==另外,进程/作业的运行时间都是由用户提供的,并不一定真实,不一定能做到真正的短作业优先。
  • 饥饿: 会导致饥饿

实现代码(非抢占式):

python 复制代码
N = 5
class PCB:
    def __init__(self):
        self.process_name = ""
        self.arrive_time = 0
        self.service_time = 0
        self.complete_time = 0


PCB_list = [PCB() for _ in range(N + 1)]

def sjf(n):

    for i in range(1, n + 1):
        print("请输入第", i, "进程名字: ", end="")
        PCB_list[i].process_name = input()
        print("请输入到达时间: ", end="")
        PCB_list[i].arrive_time = int(input())
        print("请输入服务时间: ", end="")
        PCB_list[i].service_time = int(input())

    temp = 1
    for i in range(2, n + 1):
        if PCB_list[temp].arrive_time > PCB_list[i].arrive_time or (
                PCB_list[temp].arrive_time == PCB_list[i].arrive_time and PCB_list[temp].service_time > PCB_list[
            i].service_time):
            temp = i

    PCB_list[0] = PCB_list[temp]
    PCB_list[temp] = PCB_list[1]
    PCB_list[1] = PCB_list[0]

    sum = 1
    PCB_list[1].complete_time = PCB_list[1].arrive_time + PCB_list[1].service_time
    time = PCB_list[1].complete_time
    print("\n第1次进程调度运行的进程为 :   ", PCB_list[1].process_name)

    nextprocess = 0
    while True:
        if sum >= n:
            break

        nextprocess = i = sum + 1

        while i <= n:
            if time >= PCB_list[i].arrive_time and PCB_list[i].service_time < PCB_list[nextprocess].service_time:
                nextprocess = i
            i += 1

        PCB_list[0] = PCB_list[nextprocess]
        PCB_list[nextprocess] = PCB_list[sum + 1]
        PCB_list[sum + 1] = PCB_list[0]
        nextprocess = sum + 1

        if PCB_list[nextprocess].arrive_time > time:
            time = PCB_list[nextprocess].arrive_time

        time += PCB_list[nextprocess].service_time
        PCB_list[nextprocess].complete_time = time
        sum += 1
        print("第", sum, "次进程调度运行的进程为 :   ", PCB_list[nextprocess].process_name)

    return 0


def print_info():
    i = 1
    round_time = [0] * (N + 1)
    force_round_time = [0] * (N + 1)
    sum_time = 0

    print("\n 进程   到达时间    服务时间     完成时间     周转时间   带权周转时间")

    while i < N + 1:
        round_time[i] = PCB_list[i].complete_time - PCB_list[i].arrive_time
        force_round_time[i] = round_time[i] / PCB_list[i].service_time
        print(PCB_list[i].process_name, "\t  ", PCB_list[i].arrive_time, "\t    ", PCB_list[i].service_time, "\t\t ",
              PCB_list[i].complete_time, "\t        ", round_time[i], "\t     ", force_round_time[i])
        sum_time += force_round_time[i]
        i += 1

    print("\n\n系统平均带权周转时间: ", (sum_time / (i - 1)))


if __name__ == "__main__":
    print("\t\t短作业优先调度算法")
    sjf(N)
    print_info()

输出信息:

python 复制代码
请输入第 1 进程名字: p1
请输入到达时间: 0
请输入服务时间: 5
请输入第 2 进程名字: p2
请输入到达时间: 2
请输入服务时间: 1
请输入第 3 进程名字: p3
请输入到达时间: 3
请输入服务时间: 4
请输入第 4 进程名字: p4
请输入到达时间: 5
请输入服务时间: 6
请输入第 5 进程名字: p5
请输入到达时间: 7
请输入服务时间: 8

第1次进程调度运行的进程为 :    p1
第 2 次进程调度运行的进程为 :    p2
第 3 次进程调度运行的进程为 :    p3
第 4 次进程调度运行的进程为 :    p4
第 5 次进程调度运行的进程为 :    p5

 进程   到达时间    服务时间     完成时间     周转时间   带权周转时间
p1 	   0 	     5 		  5 	         5 	      1.0
p2 	   2 	     1 		  6 	         4 	      4.0
p3 	   3 	     4 		  10 	         7 	      1.75
p4 	   5 	     6 		  16 	         11 	      1.8333333333333333
p5 	   7 	     8 		  24 	         17 	      2.125


系统平均带权周转时间:  2.1416666666666666

进程已结束,退出代码0

三、高响应比优先调度算法(HRRN)

高响应比优先算法考虑了作业的等待时间,又考虑了作业运行时间的调度算法,因此照顾了短作业,又不致使长作业等待时间过长,从而改善了处理机的调度的性能。这个算法相当于给与每个作业一个动态的优先级,这个优先级是随着时间变化的变化的,等待时间不断地增加,这将让长作业的优先级在等待期间不断地增大,等待足够的时间,必然会得到处理机。

1、该优先级的变化规则可以描述为:

2、规律:

  • 作业的等待时间相同,则要求服务的越短,优先级越高,类似于SJF算法。
  • 当要求服务的时间相同时,等得越久优先级越高,类似于FCFS算法。
  • 对于长作业来说,该算法实现了较好的折中。

3、优点:算法折中,长短作业兼顾,时间分配较为均匀。

4、缺点:每次计算响应比都会花费一定时间,即时间开销,其性能比SJF算法略差。

实现代码:

python 复制代码
class JobControl:
    def __init__(self, Id, arr_time, ser_time):
        self.Id = Id  # 序号
        self.arr_time = arr_time  # 到达时间
        self.ser_time = ser_time  # 服务时间
        self.Rp = 0
        self.state = False


jobs = []
n = 0  # 进程个数
new_time = 0

if __name__ == "__main__":
    print("请输入进程总数:")
    n = int(input())
    min_arr_time = float('inf')

    for i in range(n):
        print(f"请输入{i+1}号进程的到达时间和服务时间:")
        Id = i
        arr_time, ser_time = map(float, input().split())
        jobs.append(JobControl(Id, arr_time, ser_time))

        if arr_time < min_arr_time:
            min_arr_time = arr_time

    new_time = min_arr_time
    print("进程执行的顺序:")
    m = 0

    while m != n:
        max_Rp = -1
        max_id = 0

        for i in range(n):
            if not jobs[i].state and jobs[i].arr_time <= new_time:
                jobs[i].Rp = ((new_time - jobs[i].arr_time) + jobs[i].ser_time) / jobs[i].ser_time
                if max_Rp < jobs[i].Rp:
                    max_Rp = jobs[i].Rp
                    max_id = i

        jobs[max_id].state = True
        new_time += jobs[max_id].ser_time
        print(max_id+1, end=" ")
        m += 1

    print("\n进程的完成时间:", new_time)

输出信息:

python 复制代码
请输入进程总数:
4
请输入1号进程的到达时间和服务时间:
0 20
请输入2号进程的到达时间和服务时间:
5 15
请输入3号进程的到达时间和服务时间:
1 5
请输入4号进程的到达时间和服务时间:
15 10
进程执行的顺序:
1 3 2 4 
进程的完成时间: 50.0

进程已结束,退出代码0

四、轮转调度算法(RR)

在轮转(RR)法中,系统将所有的就绪进程按FCFS策略排成一个就绪队列。系统可设置每隔一定时间(如30 ms)便产生一次中断,去激活进程调度程序进行调度,把CPU分配给队首进程,并令其执行一个时间片。当它运行完毕后,又把处理机分配给就绪队列中新的队首进程,也让它执行一个时间片。

在RR调度算法中,应在何时进行进程的切换,可分为两种情况:

① 若一个时间片尚未用完,正在运行的进程便已经完成,就立即激活调度程序,将它从就绪队列中删除,再调度就绪队列中队首的进程运行,并启动一个新的时间片。

② 在一个时间片用完时,计时器中断处理程序被激活。如果进程尚未运行完毕,调度程序将把它送往就绪队列的末尾。

基于时间片为1的进程运行情况:

实现代码:

python 复制代码
class JC:
    def __init__(self):
        self.Id = 0
        self.arr_time = 0.0
        self.ser_time = 0.0
        self.state = False
        self.arr_s = False


jc = [JC() for _ in range(10)]
n = 0
new_time = 0.0
rr = 0.0

if __name__ == "__main__":
    min_val = 0xFFF
    print("请输入进程的总数:")
    n = int(input())
    print("请依次输入各进程的信息,按到达时间排列:")
    for i in range(n):
        print(f"请输入{i+1}号进程的到达时间和服务时间:")
        jc[i].Id = i
        jc[i].arr_time, jc[i].ser_time = map(float, input().split())

    print("请输入时间片:")
    rr = float(input())

    m = 0
    print("进程完成执行的顺序:")
    jc[0].arr_s = True
    arr_sum = 1
    ary = [-1] * 100
    ary[0] = 0
    k = 1
    q = 0

    while True:
        if jc[ary[q]].arr_s and not jc[ary[q]].state:
            if jc[ary[q]].ser_time <= rr:
                new_time += jc[ary[q]].ser_time
                jc[ary[q]].state = True
                jc[ary[q]].ser_time = 0
                print(jc[ary[q]].Id+1, end=' ')
                m += 1
                q += 1
                if arr_sum < n:
                    for i in range(arr_sum, n):
                        if jc[i].arr_time <= new_time and not jc[i].arr_s:
                            jc[i].arr_s = True
                            ary[k] = i
                            k += 1

            else:
                new_time += rr
                jc[ary[q]].ser_time -= rr
                if arr_sum < n:
                    for i in range(arr_sum, n):
                        if jc[i].arr_time <= new_time and not jc[i].arr_s:
                            jc[i].arr_s = True
                            ary[k] = i
                            k += 1
                ary[k] = ary[q]
                k += 1
                q += 1

        if m == n:
            break

    print(f"进程的完成时间: {new_time}")

输出信息:

python 复制代码
请输入进程的总数:
5
请依次输入各进程的信息,按到达时间排列:
请输入1号进程的到达时间和服务时间:
0 4
请输入2号进程的到达时间和服务时间:
1 3
请输入3号进程的到达时间和服务时间:
2 4
请输入4号进程的到达时间和服务时间:
3 2
请输入5号进程的到达时间和服务时间:
4 4
请输入时间片:
1
进程完成执行的顺序:
2 4 1 3 5 进程的完成时间: 17.0

五、最早截至时间优先算法(EDF)

最早截止期限优先(EDF)调度根据截止期限动态分配优先级。截止期限越早,优先级越高;截止期限越晚,优先级越低。根据 EDF 策略,当一个进程可运行时,它应向系统公布截止期限要求。优先级可能需要进行调整,以便反映新可运行进程的截止期限。注意单调速率调度与 EDF 调度的不同,前者的优先级是固定的。

在每一个新的就绪状态,调度器都是从那些已就绪但还没有完全处理完毕的任务中选择最早截止时间的任务,并将执行该任务所需的资源分配给它。在有新任务到来时,调度器必须立即计算EDF,排出新的定序,即正在运行的任务被剥夺,并且按照新任务的截止时间决定是否调度该新任务。如果新任务的最后期限早于被中断的当前任务,就立即处理新任务。按照EDF算法,被中断任务的处理将在稍后继续进行。

该算法的思想是从两个任务中选择截至时间最早的任务,把它暂作为当前处理任务,再判断该任务是否在当前周期内,若不在当前周期内,就让另一任务暂作当前处理任务,若该任务也不在当前周期内,就让CPU空跑到最靠近的下一个截至时间的开始,若有任务在该周期内,就判断该任务的剩余时间是否小于当前截至时间与当前时间的差,若小于,则让该任务运行到结束,否则,就让该任务运行到该周期的截止时间,就立即抢回处理器,再判断紧接着的最早截至时间,并把处理器给它,做法同上,如此反复执行。

非抢占式调度任务用于非周期实时任务:

抢占式调度任务用于周期性实时任务:

实现代码(抢占式):

python 复制代码
class JobControl:
    def __init__(self, Id, arr_time, ser_time, end_time):
        self.Id = Id
        self.arr_time = arr_time
        self.ser_time = ser_time
        self.end_time = end_time
        self.edf = -1
        self.state = False

n = 7
jc = [JobControl(0, 0, 0, 0) for _ in range(10)]
new_time = 0

print("进程总数为:" + str(n))

for i in range(5):
    jc[i].Id = i
    jc[i].arr_time = i * 20
    jc[i].ser_time = 10
    jc[i].end_time = (i + 1) * 20

for i in range(5, 7):
    jc[i].Id = i
    jc[i].arr_time = (i - 5) * 50
    jc[i].ser_time = 25
    jc[i].end_time = (i - 5 + 1) * 50

m = 0
min_val = 0xFFF
min_id = 0

print("进程完成执行的顺序:")
while True:
    for i in range(7):
        if jc[i].arr_time <= new_time and not jc[i].state and jc[i].edf == -1:
            if jc[i].end_time < min_val:
                min_val = jc[i].end_time
                min_id = jc[i].Id
                jc[i].edf = 0

    jc[min_id].ser_time -= 5
    new_time += 5

    if jc[min_id].ser_time == 0:
        jc[min_id].state = True
        print(min_id+1, end=" ")
        m += 1
        min_val = 0xFFF

    for i in range(7):
        if jc[i].arr_time <= new_time and not jc[i].state:
            if jc[i].end_time < min_val:
                min_val = jc[i].end_time
                min_id = jc[i].Id

    if m == n:
        break

print()
print("进程的完成执行时间:" + str(new_time))

输出信息:

python 复制代码
进程总数为:7
进程完成执行的顺序:
1 2 6 3 4 7 5 
进程的完成执行时间:100

六、最低松弛度优先算法(LLF)

最低松弛度优先算法(Lowest Slack Time First,简称LLF)是一种动态优先级调度算法,它根据进程的剩余处理时间和截止时间来确定优先级,以确保系统能够尽可能快地完成最紧急的任务。在LLF算法中,每个进程都有一个松弛度(Slack Time),表示当前时间到达截止时间的剩余时间。

LLF算法的核心思想是选择具有最低松弛度的进程作为下一个执行的任务,以最大限度地降低任务的响应时间和延迟。根据任务的紧急(或松弛)度,来确定任务的优先级,紧急度越高,优先级越高。要求系统中有一个按松弛度排序的实时任务就绪队列,松弛度最低的任务排在任务最前面。

  • 主要用于可抢占调度方式中
  • 松弛度=必须完成的时间 - 其本身的运行时间 - 当前时间
  • 抢占时机:松弛度为零时。
  • 最小响应时间: LLF算法优先执行剩余处理时间最短、截止时间最近的任务,因此能够最小化任务的响应时间。
  • 高实时性: LLF算法适用于对任务响应时间有严格要求的实时系统,能够确保紧急任务及时完成。
  • 负载均衡: LLF算法能够在各个任务之间实现相对均衡的负载分配,提高系统整体性能。

实现代码:

python 复制代码
class Job:
    def __init__(self):
        self.Id = 0
        self.arr_time = 0.0
        self.ser_time = 0.0
        self.end_time = 0.0
        self.llf = -1
        self.state = False

n = 6
jc = [Job() for _ in range(10)]
new_time = 0


print("进程总数:", n)

for i in range(4):
    jc[i].Id = i
    jc[i].arr_time = i * 20
    jc[i].ser_time = 10
    jc[i].end_time = (i + 1) * 20

for i in range(4, 6):
    jc[i].Id = i
    jc[i].arr_time = (i - 4) * 50
    jc[i].ser_time = 25
    jc[i].end_time = (i - 4 + 1) * 50

m = 0
min_val = float('inf')
min_id = 0

print("进程完成执行的顺序:")
while True:
    for i in range(6):
        if jc[i].arr_time <= new_time and not jc[i].state and jc[i].llf == -1:
            jc[i].llf = jc[i].end_time - jc[i].ser_time - new_time
            if jc[i].llf < min_val:
                min_val = jc[i].llf
                min_id = jc[i].Id

    jc[min_id].ser_time -= 5
    new_time += 5

    if jc[min_id].ser_time == 0:
        jc[min_id].state = True
        print(min_id+1, end=" ")
        m += 1
        min_val = float('inf')

    for i in range(6):
        if jc[i].arr_time <= new_time and not jc[i].state:
            jc[i].llf = jc[i].end_time - jc[i].ser_time - new_time
            if jc[i].llf < min_val:
                min_val = jc[i].llf
                min_id = jc[i].Id

    if m == n:
        break

print("\n")
print("进程的完成执行时间:", new_time)

输出信息:

python 复制代码
进程总数: 6
进程完成执行的顺序:
1 2 5 3 4 6 

进程的完成执行时间: 90
相关推荐
羊小猪~~2 分钟前
神经网络基础--什么是正向传播??什么是方向传播??
人工智能·pytorch·python·深度学习·神经网络·算法·机器学习
软工菜鸡28 分钟前
预训练语言模型BERT——PaddleNLP中的预训练模型
大数据·人工智能·深度学习·算法·语言模型·自然语言处理·bert
南宫生30 分钟前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
放飞自我的Coder31 分钟前
【python ROUGE BLEU jiaba.cut NLP常用的指标计算】
python·自然语言处理·bleu·rouge·jieba分词
AI视觉网奇1 小时前
sklearn 安装使用笔记
人工智能·算法·sklearn
正义的彬彬侠1 小时前
【scikit-learn 1.2版本后】sklearn.datasets中load_boston报错 使用 fetch_openml 函数来加载波士顿房价
python·机器学习·sklearn
张小生1801 小时前
PyCharm中 argparse 库 的使用方法
python·pycharm
秃头佛爷1 小时前
Python使用PDF相关组件案例详解
python
Dxy12393102161 小时前
python下载pdf
数据库·python·pdf
叶知安1 小时前
如何用pycharm连接sagemath?
ide·python·pycharm