简介
对于高性能的嵌入式应用程序所需要的对时间要求严格的响应来说,实时性能是不可缺少的,例如,电信交换设备、工业自动化和控制系统、医学监视设备和空间导航和制导系统。这样的应用程序必须在指定的时间参数以内实时地传递它们的响应。
实时性能是什么?对于 Microsoft Windows CE .NET OS,以下列表定义了实时性能:
• 有关高优先级线程调度的担保的上限 ― 只针对所有已调度线程中的最高优先级线程。
• 调度高优先级中断服务例程 (ISR) 过程中有关延迟的担保上限。抢占机制在一个短暂、有限的时间内关闭后内核有很少的空间。
• 对计划程序和它如何调度线程进行细致的控制。
重要的是应当区分实时系统和实时 OS (RTOS)。实时系统由满足系统要求所需的所有元素(硬件、OS 和应用程序)组成。RTOS 只是完整的实时系统的一个元素,它必须提供足够的功能,才能使全部实时系统能够满足它的要求。
尽管以前的 Windows CE 版本提供了某些 RTOS 功能,但自从 Windows CE 3.0 以后很多重要的内核更改极大地增强了实时性能。Windows CE .NET 内核包含了与 Windows CE 3.0 相同的实时增强功能,除此之外还有某些额外的功能。本文描述了作为 Windows CE .NET 及其以前版本的组成部分的以下更改:
Windows CE .NET
对 x86 平台添加了通过 OEM 定义的变量指定页面池大小的功能。
Windows CE 3.0
• 增加了线程优先级级别的数目(从 8 到 256)。
• 更多地控制时间和调度。应用程序可以控制提供给每个线程的时间,并操纵对它们有好处的计划程序。现在,对于与休眠和等待相关的应用程序编程接口 (API),计时器精确到一毫秒。
• 处理优先级倒置的方法得到改进。
• 全面支持嵌套中断。
• ISR 和中断服务线程 (IST) 滞后时间得到减少。
• 更细粒度的内存管理控制。
此外,本文描述了用来测试内核实时性能的工具,并提供了在三种不同 CPU 上的实时性能测试结果。
返回页首
对内核的更改
内核是 Windows CE OS 的内部核心,它负责调度和同步线程、处理异常和中断、加载应用程序和管理虚拟内存。在 Windows CE 3.0 中,为了提高性能和减少滞后时间,内核经历了以下几个更改:
• 将所有内核数据结构移动到物理内存,从而当在内核中执行非抢占代码时极大地避免了转换后备缓冲区 (TLB) 损失。
• 所有非抢占、但可中断的内核部分(称为 KCALL)被分割成更小的非抢占节。由于增加了节数,这就引入了某些复杂性,但现在抢占机制能够在更短的时间内关闭。
这一节描述为了增强 Windows CE 3.0 的实时性能对内核的进一步更改。
更多优先级级别
内核的计划程序首先使用较高的优先级级别运行某个线程,然后使用相同的优先级以循环方式运行多个线程。为线程指派优先级级别是管理执行速度的一种方式。
Windows CE 3.0 将可用于线程的优先级级别数从 8 增加到 256,0 是最高的优先级,255 是最低的优先级。Windows CE 的前一版本的优先级级别 0 到 7 对应于 Windows CE 3.0 中级别 248 到 255。更多的优先级级别允许开发人员更灵活地控制嵌入式系统的调度,并防止由于限制优先级级别数使随机应用程序降低系统性能。
要指派这些新的优先级,Windows CE 3.0 引入了两个新函数:CeSetThreadPriority 和 CeGetThreadPriority。新函数与 Windows CE 2.12 中的 SetThreadPriority 和 GetThreadPriority 函数看起来完全相同,不过新函数接受的数字范围是 0 到 255。
更多地控制时间和调度
Windows CE 3.0 已经改进了计时器性能,计时器和休眠函数调用的精度达到了一毫秒,并且应用程序可以为每个线程设置时间片。
计时器(或系统时钟)是一种速率,由 OS 以此速率生成计时器中断并对其提供服务。以前,计时器也是线程时间片,是线程没有被抢占的情况下可以在系统中运行的最长时间。在 Windows CE 3.0 中,计时器不再直接与线程时间片相关。
以前,OEM 将计时器和时间片作为 OEM 适配层 (OAL) 中的常量设置为大约 25 毫秒。计时器触发时,如果一个线程已做好准备,内核会调度此新的线程。在 Windows CE 3.0 中,计时器总是设置为一毫秒,并且可以对每个线程设置时间片。
通过将计时器从 OEM 定义的数字更改为一毫秒,可以让应用程序执行 Sleep(1) 函数,并预计得到大约一毫秒的精度。当然,这取决于线程的优先级、其他线程的优先级以及是否正在运行 ISR。以前,Sleep(1) 经过一个系统周期后返回,这意味着如果计时器被设置为 25 毫秒,则 Sleep(1) 实际上是 Sleep(25)。
计时器中断
现在,内核有几个新的变量,开发人员可以使用它们确定系统时钟是否需要重新调度。通过在适当的时候返回 SYSINTR_NOP 标志而不是 SYSINTR_RESCHED 标志,完整实现的系统时钟 ISR 可以防止内核被重新调度。Nk.lib 导出在 Timer ISR 中使用的以下变量:
• dwPreempt 是线程被抢占之前的毫秒数。
• dwSleepMin 是第一次超时(如果有)到期之前的毫秒数,需要重新调度。
• ticksleft 是已经过去、但尚未被计划程序的休眠队列处理的系统时钟数;因而,非零值将导致重新调度。
在 Timer ISR 中,其他逻辑将优化计划程序,并防止内核执行不必要的工作,如以下代码示例所示。
if (ticksleft || (dwSleepMin && (DiffMSec = dwSleepMin)) || (dwPreempt &&
(DiffMSec = dwPreempt))) return SYSINTR_RESCHED; return SYSINTR_NOP;
OEMIdle 函数
OEM 实现 OEMIdle 函数,在没有要调度的线程时内核将调用该函数。在以前的版本中,计时器时钟会强制 OS 脱离空闲状态,并返回到内核以确定是否线程已做好调度准备。如果没有线程做好准备,内核再次调用 OEMIdle。该操作将导致内核每隔 25 毫秒(或 OEM 指定的其他时间片长度)被激活一次,以确定是否仍然没有要调度的线程。在电池供电的设备上,这样的操作会耗尽宝贵的电池电量。
在 Windows CE 3.0 中,为了在时钟频率较高的情况下减少耗电量,OEMIdle 函数可以让 CPU 进入待机模式一毫秒以上。OEM 通过使用 dwSleepMin 和 DiffMSec 变量来编程设置系统时钟计时器,以便在第一个可用的超时后唤醒。DiffMSec 是自从通过 TimerCallBack 函数检索到最后一次间隔时间以来的当前毫秒值。
硬件计时器的最大超时值可能小于 MAX_DWORD 毫秒值,所以可以编程设置计时器的最大等待时间。在所有情况下,系统从空闲状态返回时,OEMIdle 函数必须使用已经过去的实际毫秒数更新 CurMSec 和 DiffMSec。CurMSec 是间隔时间的当前值 £ 即自从启动以来的毫秒数。
线程时间片
在 Windows CE 3.0 中,线程时间片很灵活,足以使应用程序能够逐个线程地设置时间片。这就让开发人员可以改编计划程序,以满足应用程序的当前需要。为了调整时间片,已经添加了两个新函数:CeGetThreadQuantum 和 CeSetThreadQuantum。这项更改使应用程序能够基于线程完成任务所需要的时间量来设置线程的时间片。通过将任何线程的线程时间片设置为零,循环调度算法可以变为“运行到完成”算法。只有较高优先级的线程或硬件中断才能先于设置为运行到完成的线程执行。
默认时间片是 100 毫秒,但在 OEM 初始化阶段,OEM 可以通过将内核变量 dwDefaultThreadQuantum 设置为大于零的任何值,从而重写系统的默认值。
更改处理优先级倒置的方法
为了有助于缩短响应时间,Windows CE 3.0 更改了它的优先级倒置方法,当低优先级线程拥有一个较高优先级线程所需要的内核对象时,就会发生优先级倒置。Windows CE 使用优先级继承来处理优先级倒置,这时,被阻塞的、拥有较高优先级线程所需要的内核对象的线程将继承更高的优先级。优先级倒置使较低优先级线程能够运行,并释放资源供较高优先级的线程使用。以前,内核处理整个倒置链。从 Windows CE 3.0 开始,内核保证只处理优先级倒置到一个级别的深度。
优先级倒置有两个基本示例。第一个是简单的情况,这种情况下,对优先级倒置的处理从 Windows CE 2.12 到 Windows CE 3.0 没有变化。例如,在有三个处于运行状态的线程时,可以看见这种情况。线程 A 的优先级是 1,线程 B 和 C 优先级较低。如果线程 A 正在运行,并且因为线程 B 拥有线程 A 需要的内核对象而使 A 被阻塞,那么线程 B 的优先级会提高到 A 的优先级级别,以便允许线程 B 运行。然后,如果因为线程 C 拥有线程 B 需要的内核对象而使线程 B 被阻塞,则线程 C 的优先级会提高到 A 的优先级级别,以便允许线程 C 也能运行。
第二个并且是更有趣的情况是,线程 A 可以以比 B 和 C 更高的优先级运行,线程 B 拥有 A 需要的内核对象,线程 B 被阻塞,等待 C 释放它需要的内核对象,而 C 正在运行。在 Windows CE 2.12 中,当 A 运行然后因为 B 而被阻塞时,B 和 C 的优先级都会提高到 A 的优先级,以便使它们能够运行。在 Windows CE 3.0 中,当 A 因为 B 而被阻塞时,只有线程 B 的优先级被提高。通过减少复杂性和更改算法,极大地减少和限制了 Windows CE 中最大的 KCALL。
中断处理和嵌套中断