实时调度约束是管理数据采集或进程控制硬件所必需的。实时环境要求进程能够在有限时间内对外部事件做出反应。旨在为一组分时进程平均分配处理资源的内核的功能中可能不具备此类约束。
本节介绍 SunOS 实时调度器及其优先级队列,以及如何使用控制调度的系统调用和实用程序。
对于实时应用程序,调度行为中最重要的元素是提供实时调度类。标准的分时调度类并不适用于实时应用程序,因为此调度类会平等对待每个进程。标准的分时调度类中的优先级概念有限。实时应用程序需要完全采用进程优先级的调度类,另外还需要进程优先级仅由显式应用程序操作更改的调度类。
术语分发延迟是指系统响应进程开始操作的请求所用的时间。使用专门根据应用程序优先级而编写的调度器,可以开发具有有限分发延迟的实时应用程序。
下图说明了应用程序响应来自外部事件的请求所用的时间。
应用程序总响应时间包括中断响应时间、分发延迟以及应用程序的响应时间。
应用程序的中断响应时间包括系统的中断延迟以及设备驱动程序自身的中断处理时间。中断延迟由系统在禁用中断时必须运行的最长间隔确定。在 SunOS 中,使用通常不需要提高处理器中断级别的同步元语可以最小化此时间。
在中断处理过程中,驱动程序的中断例程会唤醒高优先级进程并在完成时返回。系统可检测优先级高于已中断进程的进程现在是否可进行分发,然后分发此进程。将上下文从优先级较低的进程切换到优先级较高的进程所用的时间包括在分发延迟时间中。
图 10–3 说明了系统的内部分发延迟和应用程序响应时间。响应时间根据系统响应内部事件所用的时间定义。内部事件的分发延迟表示进程唤醒优先级较高的进程所需的时间。分发延迟还包括系统分发优先级较高的进程所用的时间。
应用程序响应时间是指驱动程序执行以下操作所用的时间:唤醒优先级较高的进程,释放低优先级进程中的资源,重新调度优先级较高的任务,计算响应以及分发任务。
分发延迟的时间间隔内会发生中断并可对中断进行处理。此处理会增加应用程序响应时间,但是不会影响测量分发延迟。因此,此处理不受分发延迟保证的限制。
使用实时 SunOS 提供的新调度技术,可以将系统分发延迟时间限制在指定的范围内。如下表中所示,可以通过有限进程数来改善分发延迟。
表 10–1 实时系统分发延迟
工作站 |
有限进程数 |
任意进程数 |
---|---|---|
SPARCstation 2 |
在活动进程数小于 16 的系统中,小于0.5 毫秒 |
1.0 毫秒 |
SPARCstation 5 |
小于 0.3 毫秒 |
0.3 毫秒 |
SunOS 内核按优先级分发进程。调度器或分发程序支持调度类这一概念。可以将类定义为实时类 (RT)、系统类 (SYS) 和分时类 (TS)。每个类都有一个唯一的调度策略,用于分发属于此类的进程。
内核首先分发优先级最高的进程。缺省情况下,实时进程优先于 sys 和 TS 进程。管理员可以配置系统以使 TS 进程和 RT 进程的优先级重叠。
无法由软件控制的硬件中断具有最高优先级。处理中断的例程会从中断处直接立即进行分发,而不会考虑当前进程的优先级。
实时进程具有最高的缺省软件优先级。RT 类中的进程具有优先级以及时间量程值。RT 进程将严格按照这些参数进行调度。只要 RT 进程准备运行,SYS 或 TS 进程便无法运行。通过固定优先级调度,关键进程可以按照预先确定的顺序运行,直到完成为止。这些优先级永远不会更改,除非应用程序对其进行更改。
RT 类的进程可继承父进程的时间量程(有限或无限)。具有有限时间量程的进程将一直运行到时间量程到期为止。如果具有有限时间量程的进程在等待 I/O 事件时阻塞,或者被优先级较高的可运行实时进程抢占,则该进程也会停止运行。具有无限时间量程的进程仅当终止、阻塞或被抢占时才会停止执行。
SYS 类用于调度特殊系统进程(如换页、STREAMS 和交换程序)的执行。不能将进程的类更改为 SYS 类。SYS 类的进程具有内核在进程启动时所建立的固定优先级。
分时 (TS) 进程的优先级最低。TS 类的进程以动态方式进行调度,每个时间片为数百毫秒。TS 调度器可以循环方式切换上下文,通常足以为每个进程提供平等的运行机会,具体取决于以下各项:
时间片值
进程上次休眠时所记录的进程历史记录
有关 CPU 使用率的注意事项
缺省分时策略会为优先级较低的进程提供较大的时间片。
子进程可通过 fork(2) 继承父进程的调度类和属性。通过 exec(2) 无法更改进程的调度类和属性。
每个调度类通过不同的算法分发。内核将调用与类有关的例程以做出有关 CPU 进程调度的决定。内核与类无关,并会删除队列中优先级最高的进程。每个类都负责针对属于此类的进程计算其优先级值。该值将放入此进程的分发优先级变量中。
如下图所示,每个类算法都采用不同的方法来确定将放入全局运行队列中的最高优先级进程。
每个类都具有应用于此类中的进程的一组优先级别。特定于类的映射会将这些优先级映射到一组全局优先级。不要求一组全局调度优先级映射从零开始或具有连续性。
缺省情况下,分时 (TS) 进程的全局优先级值的范围为 -20 到 +20。这些全局优先级值映射到内核 0-40,临时指定值最高为 99。实时 (RT) 进程的缺省优先级范围为 0-59,并映射到内核 100 到 159。与类无关的内核代码将运行队列中具有最高全局优先级的进程。
分发队列是指具有相同全局优先级的进程的线性链接表。每个进程都具有调用进程时附加到进程的特定于类的信息。进程将按照基于进程全局优先级的顺序从内核分发表分发。
分发进程时,会将进程的上下文连同其内存管理信息、寄存器和栈一起映射到内存。上下文映射完成之后便开始执行进程。内存管理信息采用硬件寄存器的形式,这些寄存器包含针对当前运行的进程执行虚拟内存转换所需的数据。
当可以分发优先级较高的进程时,内核便会中断其计算并强制执行上下文切换,从而抢占当前运行的进程。如果内核发现现在可以分发优先级较高的进程,则可能会随时抢占进程。
例如,假定进程 A 从外围设备执行读操作。内核会将进程 A 置于休眠状态。然后,内核发现优先级较低的进程 B 可运行。此时会分发进程 B 并开始执行此进程。最后,外围设备将中断,进程进入此设备的驱动程序。设备驱动程序使进程 A 可运行并返回。现在,内核通过处理、恢复执行已唤醒的进程 A 来抢占 B,而不是返回到已中断的进程 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 可能会指定许多 ID 类型和 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) 中找到,并会在示例 10–1 中列出。
要向系统中添加 RT 类文件,必须具有以下模块:
装入 rt_dptbl(4) 的类模块中的 rt_init() 例程。
rt_dptbl(4) 模块,用于提供分发参数以及将指针返回到 config_rt_dptbl 的例程。
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[] 值。
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, }