实时调度约束是管理数据获取或进程控制硬件所必需的。实时环境要求进程能够在限制的时间内对外部事件做出反应。此类约束可能超出设计用于为一组分时进程公平分发处理资源的内核的功能。
本节介绍 SunOS 实时调度程序、其优先级队列,以及如何使用控制调度的系统调用和实用程序。
实时应用程序调度行为中最重要的元素是置备实时调度类。标准的分时调度类不适用于实时应用程序,因为此调度类同等对待所有进程。标准的分时调度类具有受限的优先级概念。实时应用程序需要将进程优先级作为绝对优先级的调度类。实时应用程序还需要仅通过显式应用程序操作更改进程优先级的调度类。
术语分发延迟描述系统响应请求以使进程开始操作的时间。通过专门为了遵守应用程序优先级而编写的调度程序,可以使用限制的分发延迟开发实时应用程序。
下图展示了应用程序响应外部事件的请求所需的时间。
图 12-2 应用程序响应时间
总体应用程序响应时间包括中断响应时间、分发延迟和应用程序的响应时间。
应用程序的中断响应时间包括系统的中断延迟和设备驱动程序本身的中断处理时间。中断延迟由系统必须在禁用中断的情况下运行的最长间隔决定。在 SunOS 中使用通常不需要提高处理器中断级别的同步原语最大程度地减少此时间。
在中断处理期间,驱动程序的中断例程唤醒高优先级进程并在完成后返回。系统检测到某个优先级高于中断进程的进程现已做好分发准备,随即分发该进程。将上下文从较低优先级进程切换到较高优先级进程的时间包括在分发延迟时间中。
图 12-3 展示了系统的内部分发延迟和应用程序响应时间。响应时间定义为系统响应内部事件所需的时间。内部事件的分发延迟表示进程唤醒更高优先级进程所需的时间。分发延迟还包括系统分发更高优先级进程所需的时间。
应用程序响应时间是驱动程序执行以下操作所需的时间:唤醒更高优先级进程、从低优先级进程释放资源、重新调度更高优先级任务、计算响应和分发任务。
在分发延迟间隔期间可能会发生并处理中断。此处理会增加应用程序的响应时间,但是不算入分发延迟。因此,此处理不受分发延迟保证的约束。
图 12-3 内部分发延迟
使用实时 SunOS 随附的新调度技术,系统分发延迟时间在指定的范围之内。如您在下表中所见,分发延迟通过有限数量的进程进行改善。
表 12-1 实时系统分发延迟
|
SunOS 内核按优先级分发进程。调度程序或分发程序支持调度类的概念。类定义为实时 (RT)、系统 (SYS) 和分时 (TS)。每个类都具有唯一的调度策略,用于在其类中分发进程。
内核首先分发最高优先级的进程。缺省情况下,实时进程的优先级高于 sys 和 TS 进程。管理员可以配置系统,以便 TS 进程和 RT 进程的优先级重叠。
图 12-4 调度类的分发优先级
不能由软件控制的硬件中断具有最高的优先级。处理中断的例程直接从中断即时分发,而不管当前进程的优先级为何。
实时进程具有最高的缺省软件优先级。 RT 类中的进程具有优先级和时间量程值。RT 进程严格根据这些参数进行调度。只要 RT 进程准备运行,就不能运行 SYS 或 TS 进程。固定优先级调度允许重要进程按照预先确定的顺序运行,直到完成。这些优先级永远不会更改,除非由应用程序更改。
RT 类进程继承父项的时间量程,而不管是有限还是无限时间量程。具有有限时间量程的进程一直运行到时间量程到期。如果具有有限时间量程的进程在等待 I/O 事件时阻塞或被更高优先级的可运行实时进程抢占,则该进程也会停止运行。具有无限时间量程的进程仅在进程终止、阻塞或被抢占时才停止执行。
SYS 类用于调度特殊系统进程的执行,例如分页、STREAMS 和交换程序。您无法将进程的类更改为 SYS 类。进程的 SYS 类具有固定的优先级,这些优先级由内核在进程启动时建立。
分时 (TS) 进程的优先级最低。TS 类的进程以动态方式进行调度,每个时间片为数百毫秒。TS 调度程序以循环方式足够频繁地切换上下文,以便让每个进程具有同等的运行机会,具体取决于:
时间片值
记录进程上次进入睡眠状态的时间的进程历史记录
CPU 使用率注意事项
缺省的分时策略为较低优先级的进程提供较长的时间片。
子进程通过 fork(2) 继承父进程的调度类和属性。进程的调度类和属性不会被 exec(2) 更改。
不同的算法分发每个调度类。内核调用与类相关的例程以做出有关 CPU 进程调度的决定。内核与类无关,且执行其队列中最高优先级的进程。每个类负责计算该类的进程优先级值。此值会置入该进程的分发优先级变量中。
如下图所示,每个类算法都有其自己的方法来指定要置入全局运行队列的最高优先级进程。
图 12-5 内核分发队列
每个类都有一组适用于该类中进程的优先级别。特定于类的映射将这些优先级映射到一组全局优先级。一组全局调度优先级映射不需要以零开始或连续。
缺省情况下,分时 (TS) 进程的全局优先级值范围为 -20 到 +20。这些全局优先级值映射到内核中的 0-40,临时分配最高可达 99。实时 (RT) 进程的缺省优先级范围为 0-59,且映射到内核中的 100 到 159。内核的与类无关的代码运行队列中全局优先级最高的进程。
分发队列是具有相同全局优先级的进程的线性链接列表。每个进程在调用时都具有附加到该进程的特定于类的信息。进程按照基于进程全局优先级的顺序从内核分发表中分发。
分发进程时,进程的上下文与其内存管理信息、寄存器以及栈一起映射到内存。上下文映射完成之后,执行开始。内存管理信息采用硬件寄存器形式,其中包含为当前运行的进程执行虚拟内存转换所需的数据。
当较高优先级的进程变为可分发时,内核中断其计算并强制进行上下文切换,从而抢占当前运行的进程。如果内核发现目前有较高优先级的进程可分发,则可以随时抢占进程。
例如,假定进程 A 从外围设备执行读取操作。内核将进程 A 置入睡眠状态。然后,内核发现较低优先级的进程 B 可运行。进程 B 得到分发并开始执行。最后,外围设备发送中断,并进入设备的驱动程序。设备驱动程序使进程 A 可运行并返回。内核现在阻止 B 的处理,恢复已唤醒进程 A 的执行,而不是返回到中断的进程 B。
当多个进程争用内核资源时,会发生另一种有趣的情况。高优先级的实时进程可能正在等待低优先级进程持有的资源。当低优先级进程释放资源时,内核会抢占该进程以恢复较高优先级进程的执行。
当较高优先级的进程由一个或多个较低优先级进程阻塞很长时间时,会发生优先级倒置。在 SunOS 内核中使用诸如互斥锁等同步原语可能会导致优先级倒置。
当进程必须等待一个或多个进程放弃资源时,该进程处于阻塞状态。延长阻塞可能会导致错过期限,即便对于低利用等级也是如此。
针对 SunOS 内核的互斥锁的优先级倒置问题已通过实施基本的优先级继承策略得到解决。策略表明当较低优先级的进程阻塞较高优先级进程的执行时,较低优先级进程继承较高优先级进程的优先级。此继承对进程可以保持阻塞状态的时间设定一个上界。策略是内核行为的属性,而不是程序员通过系统调用或接口执行制定的解决方案。但是,用户级别的进程仍然可能出现优先级倒置。
《多线程编程指南》中的"互斥锁属性"中讨论了用户优先级倒置的问题以及处理优先级倒置的方法。
以下接口调用可控制进程调度。
通过 priocntl(2) 实现对活动类调度的控制。类属性通过 fork(2) 和 exec(2) 与优先级控制所需的调度参数和权限一起继承。此继承对 RT 和 TS 类发生。
priocntl(2) 是用于指定系统调用适用于的实时进程、一组进程或类的接口。priocntlset(2) 还提供用于指定系统调用适用于的完整进程组的更通用接口。
priocntl(2) 的命令参数可以为以下之一:PC_GETCID、PC_GETCLINFO、PC_GETPARMS 或 PC_SETPARMS。调用进程的实际或有效 ID 必须与受影响进程的实际或有效 ID 匹配,或必须具有超级用户特权。
此命令采用包含可识别类名称的结构的名称字段。将返回类 ID 以及类属性数据数组。
此命令采用包含可识别类标识符的结构的 ID 字段。将返回类名称以及类属性数据数组。
此命令返回一个指定进程的调度类标识符或类特定调度参数。尽管 idtype 和 id 指定的组可能比较大,但是 PC_GETPARMS 仅返回一个进程的参数。由类选择进程。
此命令设置指定进程的调度类或类特定调度参数。
返回指定策略的最大值。
返回指定策略的最小值。有关更多信息,请参见 sched_get_priority_max(3R) 手册页。
将指定的 timespec 结构更新为当前的执行时间限制。有关更多信息,请参见 sched_get_priority_max(3RT) 手册页。
设置或获取指定进程的调度参数。
阻塞调用进程,直到调用进程返回到进程列表的开头。
控制进程调度的管理实用程序包括 dispadmin(1M) 和 priocntl(1)。这两个实用程序通过兼容选项和可装入模块支持 priocntl(2) 系统调用。这些实用程序提供在运行时控制实时进程调度的系统管理功能。
priocntl(1) 命令设置和检索进程的调度程序参数。
dispadmin(1M) 实用程序通过包括 -l 命令行选项,在运行时显示所有当前的进程调度类。还可以使用 RT 作为实时类的参数,为 -c 选项后面指定的类更改进程调度。
dispadmin(1M) 的类选项位于以下列表中:
列出当前配置的调度程序类
使用参数指定要显示或更改的类
获取指定类的分发参数
与 -g 配合使用,指定时间量程精度
指定可在其中存放值的文件
包含分发参数的类特定文件也可以在运行时进行装入。使用此文件建立一组新的优先级,替代引导时建立的缺省值。此类特定文件必须采用 -g 选项所使用的格式声明参数。RT 类的参数在 rt_dptbl(4) 中,并在示例 12-1 中列出。
要将 RT 类文件添加到系统,以下模块必须存在:
类模块中装入 rt_dptbl(4) 的 rt_init() 例程。
提供分发参数和将指针返回到 config_rt_dptbl 的例程的 rt_dptbl(4) 模块。
dispadmin(1M) 可执行文件。
以下步骤用于安装 RT 类分发表:
使用以下命令装入类特定模块,其中 module_name 是类特定模块。
# modload /kernel/sched/module_name
调用 dispadmin 命令。
# dispadmin -c RT -s file_name
文件必须描述一个与要覆盖的表具有相同数量项目的表。
两个调度类各自关联了一个参数表:rt_dptbl(4) 和 ts_dptbl(4)。这些表可以在引导时使用可装入模块或在运行时使用 dispadmin(1M) 进行配置。
实时的核心表建立 RT 调度的属性。rt_dptbl(4) 结构包括一个参数数组 struct rt_dpent_t。n 个优先级别中的每个级别都有一个参数。指定优先级别的属性由数组中的第 i 个参数结构指定,即 rt_dptbl[i]。
参数结构包含以下成员,这些成员在 /usr/include/sys/rt.h 头文件中也有介绍。
与此优先级别关联的全局调度优先级。rt_globpri 值无法通过 dispadmin(1M) 进行更改。
分配给此级别的进程的时间量程长度(秒)。有关更多信息,请参见时间戳接口。此时间量程值对于特定级别的进程只是缺省值或起始值。实时进程的时间量程可以使用 priocntl(1) 命令或 priocntl(2) 系统调用进行更改。
实时管理员可以随时通过重新配置 config_rt_dptbl 来更改调度程序实时部分的行为。rt_dptbl(4) 手册页的标题为“替换 rt_dptbl 可装入模块”的一节中介绍了一种方法。
在正在运行的系统上检查或修改实时参数表的另一种方法是使用 dispadmin(1M) 命令。通过为实时类调用 dispadmin(1M),可以从内核的核心表中检索当前 config_rt_dptbl 配置的当前 rt_quantum 值。当覆盖当前的核心表时,用于输入到 dispadmin(1M) 的配置文件必须符合 rt_dptbl(4) 手册页中描述的特定格式。
下面是具有优先权的进程 rtdpent_t 及其关联的时间量程 config_rt_dptbl[] 值的示例(如进程可能在 config_rt_dptbl[] 中显示的一样)。
示例 12-1 RT 类分发参数
rtdpent_t rt_dptbl[] = { 129, 60, /* prilevel Time quantum */ 130, 40, 100, 100, 131, 40, 101, 100, 132, 40, 102, 100, 133, 40, 103, 100, 134, 40, 104, 100, 135, 40, 105, 100, 136, 40, 106, 100, 137, 40, 107, 100, 138, 40 108, 100, 139, 40, 109, 100, 140, 20, 110, 80, 141, 20, 111, 80, 142, 20, 112, 80, 143, 20, 113, 80, 144, 20, 114, 80, 145, 20, 115, 80, 146, 20, 116, 80, 147, 20, 117, 80, 148, 20, 118, 80, 149, 20, 119, 80, 150, 10, 120, 60, 151, 10, 121, 60, 152, 10, 122, 60, 153, 10, 123, 60, 154, 10, 124, 60, 155, 10, 125, 60, 156, 10, 126, 60, 157, 10, 126, 60, 158, 10, 127, 60, 159, 10, 128, 60, }