MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

公平性与效率:进程调度的双重目标

2022-10-316.6k 阅读

进程调度的基础概念

进程调度的定义

在操作系统中,进程调度是指操作系统内核从就绪队列中选择一个进程,并将CPU分配给它的过程。计算机系统中通常有多个进程竞争CPU资源,而CPU在某一时刻只能执行一个进程的指令。进程调度器就像是一个交通警察,决定哪个“车辆”(进程)可以进入“道路”(CPU)行驶。通过合理的调度,使得各个进程都能在一定时间内获得CPU的执行权,从而实现多任务处理。

进程调度的层次

  1. 高级调度(作业调度):也称为长程调度,它决定从外存的后备队列中选择哪些作业调入内存,并为它们创建进程。高级调度主要控制多道程序中作业进入系统的速度,对系统的并发度有重要影响。例如,在批处理系统中,作业调度会按照一定的策略(如先来先服务、短作业优先等)从等待处理的作业队列中挑选作业进入内存运行。
  2. 中级调度(内存调度):主要任务是将暂时不能运行的进程调至外存等待,此时进程状态变为挂起状态。当中断处理、对换等事件发生时,中级调度会决定将哪些挂起的进程重新调回内存,恢复到就绪状态。它对于提高内存利用率和系统吞吐量有重要作用,比如在内存资源紧张时,将一些不急需运行的进程换出到外存,腾出内存空间给更需要的进程。
  3. 低级调度(进程调度):也叫短程调度,是操作系统最基本的调度,它决定就绪队列中的哪个进程获得CPU的使用权,是最频繁执行的调度。进程调度的算法和策略直接影响系统的性能,例如实时系统中,需要快速响应某些紧急任务,低级调度就要保证这些任务能及时获得CPU资源。

进程状态与调度的关系

进程在其生命周期中会经历不同的状态,而调度操作会使进程在这些状态之间转换。常见的进程状态有:

  1. 就绪状态:进程已获得除CPU以外的所有必要资源,一旦获得CPU就可以立即执行。调度器从就绪队列中挑选进程,将其状态转换为运行状态。
  2. 运行状态:进程正在CPU上执行。在时间片轮转调度算法中,当进程的时间片用完时,调度器会将其从运行状态转换回就绪状态,重新加入就绪队列等待下一次调度。
  3. 阻塞状态:进程因等待某个事件(如I/O操作完成、信号量等)而暂时无法执行。当所等待的事件发生时,进程从阻塞状态转换为就绪状态,进入就绪队列等待调度。例如,一个进程发起了磁盘读操作,在数据未读取完成前,它会进入阻塞状态,当数据读取完成,它就会进入就绪状态。

公平性在进程调度中的体现

公平性的定义

进程调度中的公平性是指每个进程都能在合理的时间内获得CPU资源,避免某些进程长时间得不到执行而“饿死”。公平性确保了所有进程在系统资源分配上有平等的机会,不会因为某些进程的特性(如优先级、运行时间等)而使其他进程被无限期地搁置。

实现公平性的调度算法

  1. 先来先服务(FCFS, First - Come, First - Served)
    • 原理:按照进程到达就绪队列的先后顺序进行调度。最早进入就绪队列的进程优先获得CPU资源。
    • 公平性分析:从排队的角度看,FCFS算法具有一定的公平性,因为它不考虑进程的其他属性,完全按照到达顺序处理。每个进程都有机会按照其到达的先后顺序执行。例如,假设有三个进程P1、P2、P3依次在0、1、2时刻到达就绪队列,P1会先获得CPU执行,然后是P2,最后是P3。
    • 代码示例(Python模拟)
import collections


def fcfs(processes):
    total_time = 0
    for process in processes:
        print(f"执行进程 {process['name']},执行时间 {process['burst_time']}")
        total_time += process['burst_time']
        process['finish_time'] = total_time
        process['turnaround_time'] = process['finish_time'] - process['arrival_time']
        process['waiting_time'] = process['turnaround_time'] - process['burst_time']
    return processes


processes = [
    {'name': 'P1', 'arrival_time': 0, 'burst_time': 24},
    {'name': 'P2', 'arrival_time': 0, 'burst_time': 3},
    {'name': 'P3', 'arrival_time': 0, 'burst_time': 3}
]
result = fcfs(processes)
for process in result:
    print(f"进程 {process['name']},周转时间 {process['turnaround_time']},等待时间 {process['waiting_time']}")
- **缺点**:对于短进程来说,可能会因为长进程先到达而等待很长时间。例如,如果一个长进程先进入就绪队列,后续的短进程可能要等待较长时间才能执行,这在一定程度上影响了整体的公平性,因为短进程可能有更紧迫的需求。

2. 时间片轮转(RR, Round - Robin) - 原理:系统将CPU时间划分为固定大小的时间片,每个进程轮流在一个时间片内执行。当时间片用完时,无论进程是否执行完毕,都会被调度器暂停,重新加入就绪队列的末尾,等待下一轮调度。 - 公平性分析:RR算法通过固定时间片的分配,使得每个进程都能在一定时间间隔内获得CPU执行机会,避免了进程长时间等待。即使是长进程,也会被时间片的限制打断,从而让其他进程有机会运行。例如,假设有三个进程P1、P2、P3,时间片为2。P1先执行2个时间单位,然后P2执行2个时间单位,接着P3执行2个时间单位,之后P1又会获得2个时间单位的执行时间,如此循环。 - 代码示例(Python模拟)

import collections


def round_robin(processes, time_quantum):
    queue = collections.deque(processes)
    total_time = 0
    completed = []
    while queue:
        process = queue.popleft()
        if process['burst_time'] <= time_quantum:
            total_time += process['burst_time']
            process['finish_time'] = total_time
            process['turnaround_time'] = process['finish_time'] - process['arrival_time']
            process['waiting_time'] = process['turnaround_time'] - process['burst_time']
            completed.append(process)
        else:
            total_time += time_quantum
            process['burst_time'] -= time_quantum
            queue.append(process)
    return completed


processes = [
    {'name': 'P1', 'arrival_time': 0, 'burst_time': 24},
    {'name': 'P2', 'arrival_time': 0, 'burst_time': 3},
    {'name': 'P3', 'arrival_time': 0, 'burst_time': 3}
]
time_quantum = 4
result = round_robin(processes, time_quantum)
for process in result:
    print(f"进程 {process['name']},周转时间 {process['turnaround_time']},等待时间 {process['waiting_time']}")
- **缺点**:如果时间片设置过长,RR算法会退化为FCFS算法,对短进程不利;如果时间片设置过短,会导致进程上下文切换过于频繁,增加系统开销,降低系统效率。

3. 公平共享调度(FSS, Fair - Share Scheduling) - 原理:FSS算法旨在确保每个用户或用户组能够公平地共享CPU资源。它不是针对单个进程进行调度,而是将CPU资源按比例分配给不同的用户或用户组。例如,系统中有两个用户A和B,分别有3个和2个进程。如果分配给用户A的CPU资源比例为60%,分配给用户B的比例为40%,那么调度器会按照这个比例在用户A的3个进程和用户B的2个进程之间分配CPU时间。 - 公平性分析:FSS算法提供了一种宏观上的公平性,它确保不同的用户或用户组在系统资源利用上有相对公平的机会。即使某个用户的进程数量较多,但整体资源分配比例是固定的,不会让一个用户或用户组独占过多资源。 - 缺点:实现相对复杂,需要维护用户或用户组的资源分配信息,并且在动态环境下(如进程的创建和销毁),如何准确地调整资源分配比例是一个挑战。

效率在进程调度中的体现

效率的定义

进程调度中的效率主要体现在系统资源的有效利用和进程的快速响应上。高效的调度算法能够减少CPU的空闲时间,提高CPU利用率,同时尽量缩短进程的平均周转时间、平均等待时间等指标,使得系统能够在单位时间内完成更多的任务。

实现效率的调度算法

  1. 短作业优先(SJF, Shortest Job First)
    • 原理:从就绪队列中选择预计执行时间最短的进程优先执行。SJF算法基于这样的假设:短作业通常能够更快地完成,从而减少系统中进程的平均等待时间和平均周转时间。
    • 效率分析:SJF算法在理论上能够获得最小的平均周转时间和平均等待时间。因为它优先处理短作业,使得短作业能够快速完成,释放资源,让其他作业能够更快地得到执行。例如,假设有三个进程P1(执行时间24)、P2(执行时间3)、P3(执行时间3)。如果采用SJF算法,P2和P3会先执行,然后再执行P1,相比FCFS算法,整体的平均周转时间会更短。
    • 代码示例(Python模拟)
def sjf(processes):
    processes.sort(key=lambda p: p['burst_time'])
    total_time = 0
    for process in processes:
        print(f"执行进程 {process['name']},执行时间 {process['burst_time']}")
        total_time += process['burst_time']
        process['finish_time'] = total_time
        process['turnaround_time'] = process['finish_time'] - process['arrival_time']
        process['waiting_time'] = process['turnaround_time'] - process['burst_time']
    return processes


processes = [
    {'name': 'P1', 'arrival_time': 0, 'burst_time': 24},
    {'name': 'P2', 'arrival_time': 0, 'burst_time': 3},
    {'name': 'P3', 'arrival_time': 0, 'burst_time': 3}
]
result = sjf(processes)
for process in result:
    print(f"进程 {process['name']},周转时间 {process['turnaround_time']},等待时间 {process['waiting_time']}")
- **缺点**:需要预先知道每个进程的执行时间,这在实际系统中往往是难以准确获取的。而且,对于长进程来说,可能会因为不断有短进程到达而长时间得不到执行,导致“饿死”现象。

2. 优先级调度 - 原理:为每个进程分配一个优先级,调度器总是选择优先级最高的进程执行。优先级可以根据进程的类型(如系统进程优先级高于用户进程)、紧急程度等因素来确定。 - 效率分析:对于一些对时间敏感的进程(如实时进程),通过设置较高的优先级,可以确保它们能够及时获得CPU资源,从而提高系统的响应效率。例如,在一个多媒体播放系统中,音频和视频播放进程可以设置较高的优先级,以保证流畅的播放体验。 - 缺点:如果不采取适当的措施,低优先级的进程可能会被无限期地推迟执行,出现“饿死”现象。为了解决这个问题,可以采用动态优先级调整的方法,随着进程等待时间的增加,适当提高其优先级。 3. 多级反馈队列调度 - 原理:系统设置多个就绪队列,每个队列有不同的优先级和时间片。新进程首先进入最高优先级队列,按照时间片轮转方式执行。如果在一个时间片内未执行完,则降低优先级进入下一级队列。较低优先级队列的时间片会逐渐增大。调度器优先从高优先级队列中选取进程执行,只有当高优先级队列空时,才会从较低优先级队列中选取。 - 效率分析:多级反馈队列调度结合了时间片轮转和优先级调度的优点。它既能保证短进程和优先级高的进程能够快速得到执行,又能兼顾长进程的执行。例如,短进程在高优先级队列中通常能在几个时间片内完成,而长进程随着优先级降低会获得更长的时间片,减少上下文切换的开销。 - 代码示例(Python模拟)

import collections


def multi_level_feedback_queue(processes, time_quantum_list):
    queues = [collections.deque() for _ in range(len(time_quantum_list))]
    for process in processes:
        queues[0].append(process)
    total_time = 0
    completed = []
    current_queue = 0
    while True:
        if not queues[current_queue]:
            if current_queue == len(queues) - 1:
                break
            current_queue += 1
            continue
        process = queues[current_queue].popleft()
        if process['burst_time'] <= time_quantum_list[current_queue]:
            total_time += process['burst_time']
            process['finish_time'] = total_time
            process['turnaround_time'] = process['finish_time'] - process['arrival_time']
            process['waiting_time'] = process['turnaround_time'] - process['burst_time']
            completed.append(process)
        else:
            total_time += time_quantum_list[current_queue]
            process['burst_time'] -= time_quantum_list[current_queue]
            if current_queue < len(queues) - 1:
                queues[current_queue + 1].append(process)
            else:
                queues[current_queue].append(process)
    return completed


processes = [
    {'name': 'P1', 'arrival_time': 0, 'burst_time': 24},
    {'name': 'P2', 'arrival_time': 0, 'burst_time': 3},
    {'name': 'P3', 'arrival_time': 0, 'burst_time': 3}
]
time_quantum_list = [4, 8, 16]
result = multi_level_feedback_queue(processes, time_quantum_list)
for process in result:
    print(f"进程 {process['name']},周转时间 {process['turnaround_time']},等待时间 {process['waiting_time']}")

公平性与效率的平衡

平衡的必要性

在实际的操作系统中,单纯追求公平性或效率都可能导致系统性能的下降。只注重公平性,如采用严格的时间片轮转算法,可能会因为频繁的上下文切换而降低系统效率;只追求效率,如采用短作业优先算法,可能会导致长进程长时间得不到执行,引发公平性问题。因此,需要在公平性和效率之间找到一个平衡点,以满足不同应用场景的需求。

实现平衡的方法

  1. 动态调整调度算法:根据系统的运行状态和进程的特性动态选择调度算法或调整算法参数。例如,在系统负载较轻时,可以采用更注重公平性的算法,如时间片轮转;当系统负载较重时,切换到更注重效率的算法,如多级反馈队列调度,并根据进程的实时反馈动态调整优先级和时间片。
  2. 混合调度策略:结合多种调度算法的优点。例如,在Linux操作系统中,采用的完全公平调度器(CFS)结合了公平性和效率的思想。CFS基于红黑树来管理就绪队列,按照虚拟运行时间来调度进程,既保证了公平性,又通过优化数据结构和调度策略提高了效率。它为每个进程分配一个虚拟运行时间,运行时间短的进程虚拟运行时间增长慢,会优先获得调度,在一定程度上兼顾了短进程的效率,同时又通过虚拟时间的计算保证了整体的公平性。

平衡的评估指标

  1. 平均周转时间:反映了进程从提交到完成所花费的平均时间。较短的平均周转时间意味着系统能够更快地处理进程,提高了效率。但如果只追求平均周转时间的缩短而忽视公平性,可能会导致某些进程等待时间过长。
  2. 平均等待时间:体现了进程在就绪队列中等待的平均时间。降低平均等待时间有助于提高进程的响应速度,同时也是公平性的一个体现,因为等待时间过长的进程会感觉不公平。
  3. CPU利用率:表示CPU有效工作时间与总时间的比例。高CPU利用率说明系统资源得到了较好的利用,是效率的重要指标。然而,如果为了提高CPU利用率而过度牺牲公平性,可能会使一些进程长时间得不到执行。

通过综合考虑这些指标,操作系统可以在公平性和效率之间找到合适的平衡,为用户提供更优质的服务。例如,在交互式系统中,可能更注重平均等待时间和公平性,以提供流畅的用户体验;而在批处理系统中,可能更关注平均周转时间和CPU利用率,以提高系统的处理能力。在实际应用中,需要根据系统的类型、负载情况以及用户需求等多方面因素来动态调整公平性与效率的平衡。例如,在一个既有实时任务又有普通任务的系统中,实时任务需要高效的响应,此时在保证公平性的前提下,要优先满足实时任务对效率的要求,通过设置合适的优先级和调度策略来确保实时任务能够及时执行,同时又不能让普通任务等待过长时间,以维护整体的公平性。