OS-任务调度

OS-任务调度

CPU调度准则

不同的 CPU 调度算法具有不同属性,选择一个特定算法会对某些进程更为有利。为了选择算法以便用于特定情景,我们必须考虑各个算法的属性。

为了比较 CPU 调度算法,可以采用许多比较准则。选择哪些特征来比较,对于确定哪种算法是最好的有本质上的区别。这些准则包括:

  • CPU 使用率:应使 CPU 尽可能地忙碌。从概念上讲,CPU 使用率从 0% 到 100%。对于一个实际系统,它的范围应从 40%(轻负荷系统)到 90%(重负荷系统)。
  • 吞吐量:如果 CPU 忙于执行进程,那么工作就在完成。一种测量工作的方法称为吞吐量,它是在一个时间单元内进程完成的数量。对于长进程,吞吐量可能为每小时一个进程;对于短进程,吞吐量可能为每秒十个进程。
  • 周转时间:从一个特定进程的角度来看,一个重要准则是运行这个进程需要多长时间。从进程提交到进程完成的时间段称为周转时间。周转时间为所有时间段之和,包括等待进入内存、在就绪队列中等待、在 CPU 上执行和 I/O 执行。
  • 等待时间:CPU 调度算法并不影响进程运行和执行 I/O 的时间,它只影响进程在就绪队列中因等待所需的时间。等待时间为在就绪队列中等待所花时间之和。
  • 响应时间:对于交互系统,周转时间不是最佳准则。通常,进程可以相当早地产生输出,并且继续计算新的结果同时输出以前的结果给用户。因此,另一时间是从提交请求到产生第一响应的时间。这种时间称为响应时间,是开始响应所需的时间,而非输出响应所需的时间。周转时间通常受输出设备速度的限制。

最大化 CPU 使用率和吞吐量,并且最小化周转时间、等待时间和响应时间,这是可取的。在大多数情况下,优化的是平均值。然而,在有些情况下,优化的是最小值或最大值,而不是平均值。例如,为了保证所有用户都能得到好的服务,可能要使最大响应时间最小。

对于交互系统(如桌面操作系统),研究人员曾经建议最小化响应时间的方差比最小化平均响应时间更为重要。具有合理的、可预见的响应时间的系统比平均值更小但变化大的系统更为可取。不过,在 CPU 调度算法如何使得方差最小化的方面,所做的工作并不多。

业务类型

通常我们把业务划分为两种类型

  • CPU密集型
  • IO密集型
CPU密集型

CPU密集型也叫计算密集型,形容计算机内的内存和磁盘相对于CPU来说要好很多,此刻系统大部分消耗的时间都在CPU计算上,在读写内存/磁盘上消耗的时间很少,导致CPU负载就很高。就好像你用一个奔腾的CPU搭配上DDR5内存和SSD固态硬盘,虽然游戏打开的速度变得快了不少,但是进入游戏后,画面还是很卡,反应还是很慢。

IO密集型

IO密集型指系统内的CPU相对于内存和磁盘性能强不少,系统运行的时候大多数都是CPU处于等待状态,等待内存和磁盘的数据,此刻CPU负载就很低。就好像你虽然搭载了一个酷睿i7处理器,但是磁盘还是个机械硬盘,这样打开游戏的速度就很慢。

FCFS

先来先服务调度算法

毫无疑问,最简单的 CPU 调度算法是先来先服务(FCFS)调度箅法。釆用这种方案,先请求 CPU 的进程首先分配到 CPU。

FCFS调度可以通过FIFO的数据结构要就是队列的方式来实现。当任务被激活,将任务push到队尾,当调度发生,就从队列头pop一个任务去执行。

FCFS策略的特点:由于FCFS是由加入的顺序决定调度顺序,如果其中有一个任务占用时间过长,后面的任务的等待时间就会很长,造成所有任务的平均的等待时间很长。

在实际场景中,假设有一个 CPU 密集型进程和多个 I/O 密集型进程。随着进程在系统中运行,可能发生如下情况:CPU 密集型进程得到 CPU,并使用它。在这段时间内,所有其他进程会处理完它们的 I/O,并转移到就绪队列来等待 CPU。当这些进程在就绪队列中等待时,I/O 设备空闲。
最终,CPU 密集型进程完成 CPU 执行并且移到 I/O 设备。所有 I/O 密集型进程,由于只有很短的 CPU 执行,故很快执行完并移回到 I/O 队列。这时,CPU 空闲。之后,CPU 密集型进程会移回到就绪队列并分配到 CPU。再次,所有 I/O 进程会在就绪队列中等待 CPU 密集型进程的完成。由于所有其他进程都等待一个大进程释放 CPU,故称之为护航效果。与让较短进程先进行相比,这会导致 CPU 和设备的使用率降低。

FCFS 调度算法是非抢占的。一旦 CPU 分配给了一个进程,该进程就会使用 CPU 直到释放 CPU 为止,即程序终止或是请求 I/O。FCFS 算法对于分时系统(每个用户需要定时得到一定的 CPU 时间)是特别麻烦的。允许一个进程使用 CPU 过长将是个严重错误。

SJF

最短作业优先调度算法

最短作业优先(SJF)调度算法将每个进程与其下次 CPU 执行的长度关联起来。当 CPU 变为空闲时,它会被赋给具有最短 CPU 执行的进程。如果两个进程具有同样长度的 CPU 执行,那么可以由 FCFS 来处理。

一个更为恰当的表示是最短下次CPU执行算法,这是因为调度取决于进程的下次 CPU 执行的长度,而不是其总的长度。

SJF 算法的真正困难是如何知道下次 CPU 执行的长度。

SJF 算法可以是抢占的或非抢占的。当一个新进程到达就绪队列而以前进程正在执行时,就需要选择了。新进程的下次 CPU 执行,与当前运行进程的尚未完成的 CPU 执行相比,可能还要小。抢占 SJF 算法会抢占当前运行进程,而非抢占 SJF 算法会允许当前运行进程以先完成 CPU 执行。抢占 SJF 调度有时称为最短剩余时间优先调度。

SJF调度算法的问题:
实现SJF调度算法需要知道作业所需运行时间,否则调度就没有依据,要精确知道一个作业的运行时间是办不到的。

RMS

单调速率调度算法

单调速率调度算法采用抢占的、静态优先级的策略,调度周期性任务。

当较低优先级的进程正在运行并且较高优先级的进程可以运行时,较高优先级进程将会抢占低优先级。在进入系统时,每个周期性任务会分配一个优先级,它与其周期成反比,即周期越短,优先级越高;周期越长,优先级越低。

这种策略背后的理由是:更频繁地需要 CPU 的任务应分配更高的优先级。此外,单调速率调度假定:对于每次 CPU 执行,周期性进程的处理时间是相同的。也就是说,在每次进程获取 CPU 时,它的 CPU 执行长度是相同的。

EDF

最早截止时间优先调度算法

最早截止期限优先(EDF)调度根据截止期限动态分配优先级。截止期限越早,优先级越高;截止期限越晚,优先级越低。

根据 EDF 策略,当一个进程可运行时,它应向系统公布截止期限要求。优先级可能需要进行调整,以便反映新可运行进程的截止期限。

EDF 调度具有吸引力的地方是,它是理论上最佳的。从理论上说,它可以调度进程,使得每个进程都可以满足截止期限的要求并且 CPU 利用率将会是 100%。然而,在实际中,由于进程的上下文切换和中断处理的代价,这种级别的 CPU 利用率是不可能的。

RR

时间片轮转调度算法

时间片轮转(RR)调度算法是专门为分时系统设计的。它类似于 FCFS调度,但是增加了抢占以切换进程。

该算法中,将一个较小时间单元定义为时间量或时间片。时间片的大小通常为 10~100ms。就绪队列作为循环队列。CPU 调度程序循环整个就绪队列,为每个进程分配不超过一个时间片的 CPU。

为了实现 RR 调度,我们再次将就绪队列视为进程的 FIFO 队列。新进程添加到就绪队列的尾部。CPU 调度程序从就绪队列中选择第一个进程,将定时器设置在一个时间片后中断,最后分派这个进程。

接下来,有两种情况可能发生。进程可能只需少于时间片的 CPU 执行。对于这种情况,进程本身会自动释放 CPU。调度程序接着处理就绪队列的下一个进程。否则,如果当前运行进程的 CPU 执行大于一个时间片,那么定时器会中断,进而中断操作系统。然后,进行上下文切换,再将进程加到就绪队列的尾部,接着 CPU 调度程序会选择就绪队列内的下一个进程。

在 RR 调度算法中,没有进程被连续分配超过一个时间片的 CPU(除非它是唯一可运行的进程)。如果进程的 CPU 执行超过一个时间片,那么该进程会被抢占,并被放回到就绪队列。因此,RR调度算法是抢占的。

RR 算法的性能很大程度取决于时间片的大小。在一种极端情况下,如果时间片很大,那么 RR 算法与 FCFS 算法一样。相反,如果时间片很小(如 1ms),那么 RR 算法可以导致大量的上下文切换。

MFQ

多级队列调度算法

多级队列调度算法将就绪队列分成多个单独队列(图 1)。根据进程属性,如内存大小、进程优先级、进程类型等,一个进程永久分到一个队列,每个队列有自己的调度算法。

例如,可有两个队列分别用于前台进程和后台进程。前台队列可以采用 RR 调度算法,而后台队列可以采用 FCFS 调度算法。

此外,队列之间应有调度,通常采用固定优先级抢占调度。例如,前台队列可以比后台队列具有绝对的优先。

通常在使用多级队列调度算法时,进程进入系统时被永久地分配到某个队列。例如,如果前台和后台进程分别具有单独队列,那么进程并不从一个队列移到另一个队列,这是因为进程不会改变前台或后台的性质。这种设置的优点是调度开销低,缺点是不够灵活。

相反,多级反馈队列调度算法允许进程在队列之间迁移。这种想法是,根据不同 CPU 执行的特点来区分进程。如果进程使用过多的 CPU 时间,那么它会被移到更低的优先级队列。这种方案将 I/O 密集型和交互进程放在更高优先级队列上。 此外,在较低优先级队列中等待过长的进程会被移到更高优先级队列。这种形式的老化可阻止饥饿的发生。

多处理器调度

迄今为止,我们主要集中讨论单处理器系统的 CPU 调度问题。如果有多个 CPU,则负载分配成为可能,但是调度问题就相应地更为复杂。许多可能的方法都已试过,但与单处理器调度一样,没有最好的解决方案

多处理器调度的方法

对于多处理器系统,CPU 调度的一种方法是让一个处理器(主服务器)处理所有调度决定、I/O 处理以及其他系统活动,其他的处理器只执行用户代码。这种非对称多处理很简单,因为只有一个处理器访问系统数据结构,减少了数据共享的需要。

第二种方法是使用对称多处理(SMP),即每个处理器自我调度。所有进程可能处于一个共同的就绪队列中,或每个处理器都有它自己的私有就绪进程队列。不管如何,调度这样进行:每个处理器的调度程序都检查共同就绪队列,以便选择执行一个进程。如果多个处理器试图访问和更新一个共同的数据结构,那么每个处理器必须仔细编程,必须确保两个处理器不会选择同一进程,而且进程不会从队列中丢失。

几乎所有现代操作系统,包括 Windows、Linux 和 Mac OSX,都支持 SMP

SMP-处理器亲和性

考虑一下,当一个进程运行在一个特定处理器上时缓存会发生些什么。进程最近访问的数据更新了处理器的缓存。结果,进程的后续内存访问通常通过缓存来满足。

现在考虑一下,如果进程移到其他处理器上则会发生什么。第一个处理器缓存的内容应设为无效,第二个处理器缓存应重新填充。由于缓存的无效或重新填充的代价高,大多数 SMP 系统试图避免将进程从一个处理器移到另一个处理器,而是试图让一个进程运行在同一个处理器上。这称为处理器亲和性,即一个进程对它运行的处理器具有亲和性。

处理器的亲和性具有多种形式。当一个操作系统试图保持进程运行在同一处理器上时(但不保证它会这么做),这种情况称为软亲和性。这里,操作系统试图保持一个进程在某个处理器上,但是这个进程也可迁移到其他处理器。相反,有的系统提供系统调用以便支持硬亲和性,从而允许某个进程运行在某个处理器子集上。

许多系统提供软的和硬的亲和性。例如,Linux 实现软亲和性,但是它也提供系统调用 sched_setaffinity() 以支持硬亲和性。

NUMA 与 CPU 调度如下

在这里插入图片描述

系统的内存架构可以影响处理器的亲和性。图 1 为采用非统一内存访问的一种架构,其中一个 CPU 访问内存的某些部分会比其他部分更快。通常情况下,这类系统包括组合 CPU 和内存的板卡。每个板的 CPU 访问本板内存快于访问其他板的内存。

如果操作系统的 CPU 调度和内存分配算法一起工作,那么当一个进程分配到一个特定的亲和处理器时,它应分配到同板上的内存。这个例子还说明操作系统通常不按教科书描述的那样清楚地定义与实现。实际上,操作系统的各个部分的“实线”通常应是“虚线”,因为有些算法创建连接以便优化性能和可靠性。

SMP-负载均衡

对于 SMP 系统,重要的是保持所有处理器的负载平衡,以便充分利用多处理器的优点。否则,一个或多个处理器会空闲,而其他处理器会处于高负载状态,且有一系列进程处于等待状态。

负载平衡设法将负载平均分配到 SMP 系统的所有处理器。

需要注意的是,对于有些系统(它们的处理器具有私有的可执行进程的队列),负载平衡是必需的。

而对于具有公共队列的系统,负载平衡通常没有必要,因为一旦处理器空闲,它立刻从公共队列中取走一个可执行进程。同时,要注意对于大多数支持SMP的现代操作系统,每个处理器都有一个可执行进程的私有队列。

负载平衡通常有两种方法:推迁移和拉迁移:

  • 对于推迁移,一个特定的任务周期性地检查每个处理器的负载,如果发现不平衡,那么通过将进程从超载处理器推到空闲或不太忙的处理器,从而平均分配负载。
  • 对于拉迁移,当空闲处理器从一个忙的处理器上拉一个等待任务时,发生拉迁移。

推迁移和拉迁移不必相互排斥,事实上,在负载平衡系统中它们常被并行实现。例如,Linux 调度程序和用于 FreeBSD 系统的 ULE 调度程序实现了这两种技术。

有趣的是,负载平衡往往会抵消处理器亲和性的好处。也就是说,保持一个进程运行在同一处理器上的好处是进程可以利用它在该处理器缓存内的数据。无论是从一个处理器向另一处理器推或拉进程,都会失去这个好处。

与通常的系统工程情况一样,关于何种方式是最好的,没有绝对规则。因此,在某些系统中,空闲的处理器总是会从非空闲的处理器中拉进程;而在其他系统中,只有当不平衡达到一定程度后才会移动进程。

CFS

完全公平调度算法

Linux 进程调度有一个有趣历史。在 2.5 版本之前,Linux 内核采用传统 UNIX 调度算法。然而,由于这个算法并没有考虑 SMP 系统,因此它并不足够支持 SMP 系统。此外,当有大量的可运行进程时,系统性能表现欠佳。

在内核 V2.5 中,调度程序进行了大改,采用了称为 O(1) 的调度算法,它的运行时间为常量,与系统内任务数量无关。O(1) 调度程序也增加了对 SMP 系统的支持,包括处理器亲和性和处理器间的负载平衡。然而,在实践中,虽然在 SMP 系统上 O(1) 调度程序具有出色的性能,但是在许多桌面计算机系统上交互进程的响应时间却欠佳。

在内核 V2.6 的开发中,调度程序再次修改;在内核 V2.6.23 的发布中,完全公平调度程序(CFS)成为默认的 Linux 调度算法。

Linux 标准内核实现两个调度类:采用 CFS 调度算法的默认调度类和实时调度类。这里分别讨论这些。当然,新调度类也可添加。

CFS 调度程序并不采用严格规则来为一个优先级分配某个长度的时间片,而是为每个任务分配一定比例的 CPU 处理时间。每个任务分配的具体比例是根据友好值来计算的。友好值的范围从 -20 到 +19,数值较低的友好值表示较高的相对优先级。具有较低友好值的任务,与具有较高友好值的任务相比,会得到更高比例的处理器处理时间。默认友好值为 0。

友好一词源自如下想法:当一个任务增加了它的友好值,如从 0 至 +10,该任务通过降低优先级,进而对其他任务更加友好。

CFS 没有使用离散的时间片,而是采用目标延迟,这是每个可运行任务应当运行一次的时间间隔。根据目标延迟,按比例分配 CPU 时间。除了默认值和最小值外,随着系统内的活动任务数量超过了一定阈值,目标延迟可以增加。

CFS 调度程序没有直接分配优先级。相反,它通过每个任务的变量 vruntime 以便维护虚拟运行时间,进而记录每个任务运行多久。虚拟运行时间与基于任务优先级的衰减因子有关,更低优先级的任务比更高优先级的任务具有更高衰减速率。对于正常优先级的任务(友好值为 0),虚拟运行时间与实际物理运行时间是相同的。

因此,如果一个默认优先级的任务运行 200ms,则它的虚拟运行时间也为 200ms。然而,如果一个较低优先级的任务运行 200ms,则它的虚拟运行时间将大于 200ms。同样,如果一个更高优先级的任务运行 200ms,则它的虚拟运行时间将小于 200ms。当决定下步运行哪个任务时,调度程序只需选择具有最小虚拟运行时间的任务。此外,一个更高优先级的任务如成为可运行,就会抢占低优先级任务。

下面分析一下 CFS 调度程序是如何工作的。假设有两个任务,它们具有相同的友好值。一个任务是 I/O 密集型而另一个为 CPU 密集型。通常,I/O 密集型任务在运行很短时间后就会阻塞以便等待更多的 I/O;而 CPU 密集型任务只要有在处理器上运行的机会,就会用完它的时间片。

因此,I/O 密集型任务的虚拟运行时间最终将会小于 CPU 密集型任务的,从而使得 I/O 密集型任务具有更高的优先级。这时,如果 CPU 密集型任务在运行,而 I/O 密集型任务变得有资格可以运行(如该任务所等待的 I/O 已成为可用),那么 I/O 密集型任务就会抢占 CPU 密集型任务。

Linux 也实现了实时调度。采用 SCHED_FIFO 或 SCHED_RR 实时策略来调度的任何任务,与普通(非实时的)任务相比,具有更高的优先级。

Linux 采用两个单独的优先级范围,一个用于实时任务,另一个用于正常任务。实时任务分配的静态优先级为 0〜99,而正常任务分配的优先级为 100〜139。

这两个值域合并成为一个全局的优先级方案,其中较低数值表明较高的优先级。正常任务,根据它们的友好值,分配一个优先级;这里 -20 的友好值映射到优先级 100,而 +19 的友好值映射到 139。图 1 显示了这个方案。

在这里插入图片描述

cfs性能

Linux CFS 调度程序釆用高效算法,以便选择运行下个任务。每个可运行的任务放置在红黑树上(这是一种平衡的、二分搜索树,它的键是基于虚拟运行时间的)。
这种树如下图所示:

在这里插入图片描述

当一个任务变成可运行时,它被添加到树上。当一个任务变成不可运行时(例如,当阻塞等待 I/O 时),它从树上被删除。一般来说,得到较少处理时间的任务(虚拟运行时间较小)会偏向树的左侧;得到较多处理时间的任务会偏向树的右侧。

根据二分搜索树的性质,最左侧的结点有最小的键值;从 CFS 调度程序角度而言,这也是具有最高优先级的任务。由于红黑树是平衡的,找到最左侧结点会需要 O(lgN) 操作(这里 N 为树内结点总数)。不过,为高效起见,Linux 调度程序将这个值缓存在变量 rb_leftmost 中,从而确定哪个任务运行只需检索缓存的值。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值