编程接口指南

第 3 章 进程调度器

本章介绍进程的调度以及如何修改调度。

本章内容适用于相对缺省调度所提供的控制而言需要更多地控制进程执行顺序的开发者。有关多线程调度的说明,请参见《多线程编程指南》

调度器概述

创建进程时,系统将为其指定一个轻量级进程 (Lightweight Process, LWP)。如果此进程是多线程进程,则可能会为其指定更多 LWP。LWP 是被 UNIX 系统调度器所调度的对象,它确定了何时运行进程。调度器可维护基于配置参数、进程行为和用户请求的进程优先级。调度器使用这些优先级来确定下一个要运行的进程。共有六种优先级类,分别是实时、系统、交互 (interactive, IA)、固定优先级 (fixed-priority, FX)、公平份额 (fair-share, FSS) 和分时 (time-sharing, TS)。

缺省调度是一种分时策略。此策略可动态调整进程优先级,以平衡交互式进程的响应时间,另外还可动态调整优先级,以平衡使用大量 CPU 时间的进程的吞吐量。分时类的优先级最低。

SunOS 5.10 调度器还提供了一种实时调度策略。通过实时调度,用户可以为特定进程指定固定优先级。可运行的优先级最高的实时用户进程总是会获取 CPU。

SunOS 5.10 调度器还提供了一种固定优先级调度策略。通过固定优先级调度,用户可以为特定进程指定固定优先级。缺省情况下,固定优先级调度使用的优先级范围与分时调度类相同。

编写程序时,可以保证实时进程从系统及时获取响应。有关详细信息,请参见第 10 章,实时编程和管理

仅在极少数情况下才需要实时调度所提供的进程调度控制。但是,如果某个程序的要求中包括严格的时间安排约束,则可能只有实时进程才可以满足这些约束。


注意 – 注意 –

不加考虑地使用实时进程可能会对分时进程的性能产生严重的负面影响。


由于调度器管理的更改会影响调度器的行为,因此程序员可能还需要对调度器管理有一些了解。以下接口会影响调度器管理:

创建进程时,进程将继承其调度参数,包括调度类以及此类中的优先级。进程仅在用户请求时才会更改类。系统调整进程优先级的依据是用户的请求以及与该进程的调度器类相关联的策略。

在缺省配置中,初始化进程属于分时类。因此,所有用户登录 shell 都以分时进程开始。

调度器将特定于类的优先级转换为全局优先级。进程的全局优先级可确定何时运行此进程。调度器始终运行全局优先级最高的可运行进程。优先级越高,运行时间便越早。指定给 CPU 的进程在进程休眠、用完其时间片或被优先级更高的进程抢占之前会一直运行。具有相同优先级的进程按顺序循环运行。

所有实时进程的优先级都高于内核进程的优先级,所有内核进程的优先级都高于分时进程的优先级。


注 –

在单处理器系统中,如果存在可运行的实时进程,则不会运行任何内核进程和分时进程。


管理员可在配置表中指定缺省时间片。 用户可以按进程为实时进程指定时间片。

使用 ps(1) 命令的 -cl 选项可以显示进程的全局优先级。使用 priocntl(1) 命令和 dispadmin(1M) 命令可以显示有关特定于类的优先级的配置信息。

以下各节将介绍六种调度类的调度策略。

分时类

分时策略的目标是为交互式进程提供快速响应,并为计算密集 (CPU-bound) 的进程提供大吞吐量。调度器会按合适的频率切换 CPU 分配,既保证提供快速响应,同时又不使系统花费大量时间进行切换。时间片通常只有几百毫秒。

分时策略可动态更改优先级,并指定不同长度的时间片。对于使用 CPU 较短时间后便休眠的进程,调度器会提升其优先级。例如,进程启动 I/O 操作(如终端读取或磁盘读取)时会进入休眠状态。频繁休眠是交互式任务(如编辑和运行简单的 shell 命令)的特点。对于长时间使用 CPU 而不休眠的进程,分时策略会降低其优先级。

分时策略缺省情况下会为优先级较低的进程提供较大的时间片。低优先级进程可能是计算密集 (CPU-bound) 的进程。其他进程将首先获取 CPU,但是当优先级低的进程最终获取 CPU 之后,此进程会获取较大的时间片。但是,如果优先级较高的进程在某个时间片内进入可运行状态,则优先级较高的进程会抢占运行的进程所获取的 CPU。

全局进程优先级和用户提供的优先级按升序排列:优先级越高,运行时间便越早。用户优先级的范围从负的配置相关最大值到正的配置相关最大值。进程可继承其用户的优先级。缺省的初始用户优先级为零。

“用户优先级限制”是指用户优先级的配置相关最大值。可以将用户优先级设置为低于用户优先级限制的任意值。如果具有适当的权限,则可以提升用户优先级限制。缺省的用户优先级限制为零。

您可以降低进程的用户优先级,以减少进程对 CPU 的访问。或者,如果具有适当的权限,则还可以提升用户优先级以获取更快的服务。但是不能将用户优先级设置为高于用户优先级限制的值。因此,如果用户优先级限制和用户优先级的缺省值均为零,则必须先提升用户优先级限制才能提升用户优先级。

管理员配置最高用户优先级时可以不考虑全局分时优先级。例如,在缺省配置中,用户可以在–20 到 +20 范围内设置用户优先级。但是,此时会设置 60 种分时全局优先级。

调度器使用分时参数表 ts_dptbl(4) 中的可配置参数来管理分时进程。该表包含特定于分时类的信息。

系统类

系统类使用固定优先级策略运行内核进程(如服务器)和内务处理进程(如换页守护进程)。系统类保留供内核使用。用户不能向系统类中添加进程,也不能从系统类中删除进程。系统类进程的优先级在内核代码中进行设置。系统进程的优先级一经建立便不再更改。在内核模式下运行的用户进程不属于系统类。

实时类

实时类使用具有固定优先级的调度策略,以便关键进程按照预先确定的顺序运行。除非用户请求进行更改,否则实时优先级永远不会更改。特权用户可以使用 priocntl(1) 命令或 priocntl(2) 接口指定实时优先级。

调度器使用实时参数表 rt_dptbl(4) 中的可配置参数来管理实时进程。该表包含特定于实时类的信息。

交互式类

IA 类与 TS 类非常相似。如果将交互式类与窗口系统一起使用,则在侧重于输入的窗口中运行时,进程会具有较高的优先级。系统运行窗口系统时,IA 类即为缺省类。否则,IA 类会与 TS 类完全相同,并且这两种类共享同一个 ts_dptbl 分发参数表。

公平份额类

FSS 类由公平份额调度器 (FSS(7)) 使用,通过将 CPU 资源份额显式分配给各项目来管理应用程序性能。一份额表明项目有权使用的可用的 CPU 资源。系统会跟踪一段时间内的资源使用情况。如果使用大量资源,则系统将削弱使用权。如果使用较少的资源,则系统将增强使用权。FSS 根据进程属主的使用权在进程之间调度 CPU 时间,而不会考虑每个项目拥有的进程数。FSS 类使用的优先级范围与 TS 类和 IA 类相同。有关更多详细信息,请参见 FSS 手册页。

固定优先级类

FX 类提供了固定优先级抢占调度策略。此策略由需要通过用户或应用程序控制调度优先级(而不是通过系统动态调整优先级)的进程使用。缺省情况下,FX 类的优先级范围与 TS 类、IA 类和 FSS 类相同。FX 类允许用户或应用程序通过指定给此类中的进程的用户优先级值来控制调度优先级。这些用户优先级值可确定某个固定优先级进程相对于其所属类中的其他进程的调度优先级。

调度器使用固定优先级分发参数表 fx_dptbl(4) 中的可配置参数来管理固定优先级进程。该表包含特定于固定优先级类的信息。

命令和接口

下图说明了缺省的进程优先级。

图 3–1 进程优先级(从程序员的角度考虑)

实时线程优先于系统线程。系统线程优先于分时线程。每种类均有一个单独的运行队列。

进程优先级仅在调度器类的上下文中才有意义。可以通过指定类和特定于类的优先级值来指定进程优先级。系统会将类和特定于类的值映射成其用来调度进程的全局优先级。

从系统管理员的角度考虑的优先级与从用户或程序员的角度考虑的优先级不同。配置调度器类时,管理员直接处理全局优先级。系统会将用户提供的优先级映射成这些全局优先级。有关优先级的更多信息,请参见《系统管理指南:基本管理》

带有 -cel 选项的 ps(1) 命令可用于报告所有活动进程的全局优先级。priocntl(1) 命令可用于报告用户和程序员使用的特定于类的优先级。

priocntl(1) 命令以及 priocntl(2)priocntlset(2) 接口用于设置或检索进程的调度器参数。使用上述命令和两个接口设置优先级的顺序基本相同:

  1. 指定目标进程。

  2. 指定要用于这些进程的调度器参数。

  3. 执行命令或接口,为进程设置参数。

进程 ID 是 UNIX 进程的基本属性。有关更多信息,请参见 Intro(2)。类 ID 是指进程的调度器类。priocntl(2) 仅适用于分时类和实时类,不适用于系统类。

priocntl 用法

priocntl(1) 实用程序在调度进程时可执行四个不同的控制接口:

priocntl -l

显示配置信息

priocntl -d

显示进程的调度参数

priocntl -s

设置进程的调度参数

priocntl -e

执行带有指定调度参数的命令

以下示例说明了 priocntl(1) 的用法。

priocntl(1) 包括 nice(1) 的接口。nice 仅适用于分时进程,并会使用较大的数字指定较低的优先级。上一个示例相当于使用 nice(1) 将增量设置为 10:


$ nice -10 make bigprog

priocntl 接口

priocntl(2) 用于管理一个或一组进程的调度参数。可以针对 LWP、单个进程或一组进程调用 priocntl(2)。一组进程可以通过父进程、进程组、会话、用户、组、类或所有活动进程进行标识。有关更多详细信息,请参见 priocntl 手册页。

如果给定类 ID,则 PC_GETCLINFO 命令可以获取调度器类名称和参数。使用此命令,在编写程序时就不用假设需要对哪些类进行配置。

PC_SETXPARMS 命令用于设置一组进程的调度器类和参数。idtypeid 输入参数用于指定要更改的进程。

与其他接口交互

更改 TS 类中的某个进程的优先级会影响此 TS 类中其他进程的行为。本节介绍会影响其他进程的调度更改方法。

内核进程

内核守护进程和内务处理进程是系统调度器类的成员。用户既不能在此类中添加或删除进程,也不能更改这些进程的优先级。命令 ps -cel 可用于列出所有进程的调度器类。运行带有 -f 选项的 ps(1) 时,可以通过 CLS 列中的 SYS 项识别系统类中的进程。

使用 forkexec

执行 fork(2)exec(2) 接口时将继承调度器类、优先级和其他调度器参数。

使用 nice

nice(1) 命令和 nice(2) 接口的使用方式与在早期版本的 UNIX 系统中的使用方式相同。使用这些命令可以更改分时进程的优先级。使用较小的数字值可为这些接口指定较高的分时优先级。

要更改进程的调度器类或指定实时优先级,请使用 priocntl(2)。使用较大的数字值可指定较高的优先级。

init(1M)

init(1M) 进程是调度器的一个特例。要更改 init(1M) 的调度属性,init 必须是 idtypeid 或者 procset 结构所指定的唯一进程。

调度和系统性能

由于调度器会用于确定何时运行进程以及运行多长时间,因此其行为会严重影响系统的性能。

缺省情况下,所有用户进程都是分时进程。进程只能通过 priocntl(2) 调用来更改类。

所有实时进程的优先级都高于分时进程的优先级。如果任何实时进程都可以运行,则不能运行分时进程或系统进程。有时无法放弃 CPU 控制权的实时应用程序可能会完全禁止其他用户和基本内核内务处理对其进行使用。

除了控制进程类和优先级之外,实时应用程序还必须控制影响其性能的其他因素。性能方面最重要的因素包括 CPU 处理能力、主存储器量和 I/O 吞吐量。这些因素相互作用相互影响。sar(1) 命令包含用于报告所有性能因素的选项。

进程状态转换

具有严格实时约束的应用程序可能需要阻止将进程换出或换页到辅助存储器。下图说明了 UNIX 进程状态以及状态之间转换的简要概况。

图 3–2 进程状态转换图

 正在运行的进程可以通过抢占内存在内存中进入可运行状态,也可以在内存中进行休眠。内存中的进程可以交换。

活动进程通常处于图中的五种状态之一。箭头指示了进程状态如何进行变化。

当进程可以再次运行时,换页和交换都会造成延迟。对于具有严格的时间安排要求的进程而言,这种延迟是不可接受的。

为了避免交换延迟,实时进程永远不能进行交换,尽管某些实时进程可以进行换页。程序可以通过将其文本和数据锁入到主存储器中来阻止换页和交换。有关更多信息,请参见 memcntl(2) 手册页。可以锁定的内存量受限于已配置的内存量。另外,锁定过多内存可能会对未将文本和数据锁入内存的进程造成无法忍受的延迟。

实时进程与其他进程之间的性能权衡取决于局部需求。在某些系统中,可能需要进行进程锁定才能保证必需的实时响应。


注 –

有关实时应用程序延迟的信息,请参见分发延迟