翻译:Windows and Real-Time——Daniel Terhell

Windows不是实时操作系统”,这句话反复在NTDEV论坛中被提到。当某个人尝试为非Windows兼容的设备(比如一个期望软件在很短的时间片之内响应的设备)写一个插件的时候,通常会遇到这个问题。
 
实时操作系统的定义是,在满足最低要求的情况下,以可预计的方式执行。它必须严格确保在很短的时间片之内响应请求。通常可以分为软实时和硬实时环境。对于软实时,最重要的是快速响应,一次请求丢失不会造成整体失败。在硬实时环境中,作为对比,不允许任何意料之外的延时,任何请求丢失意味着整体失败。通常这样的环境都支持实时任务。
 
在Windows里,所有对操作系统的请求都基于最佳效果来处理,不保证响应时间。因此,Windwos能够在1秒钟时间内处理成千上万的请求,简单来说,它不能确保每个请求会在指定的时间片中被处理。
 
在大多数使用中,Windows是基于性能和吞吐量来设计和优化的,不是为了实时任务和低延迟,这通常是有冲突的。针对大多数使用所关注的平均吞吐性能的优化方案,和针对实时请求关注的最大响应时间的解决方案相比,最糟糕的结果是请求丢失。这意味着每一个独立的请求或操作都很重要。
 
有很多实时请求相关的解决方案,范围从工业、医疗、军工、航空、科研到高精度多媒体应用。举个简单的要求实时性的应用,“实时音频”,比如软件合成器,需要发声以响应MIDI键盘的按键,延时不能超过几毫秒。音频流必须是连续的,任何单击、弹出和释放都可能中断音频流,影响音频结果。这意味着所有的请求都符合截止时间。
 
事实上,实时应用中的延时问题,比如“在发声之前按键”延时问题,原来就有,而不是计算机时代的产物。比如说,风琴手,必须预读并在好几秒钟后才能听到他们演奏的声音,空气必须经过复杂的机制在管道中流动,声音必须从教堂的角落回传到耳朵。
 
在使用声音合成器和音频插件时,实时音频应用中的延时问题,通常有他们用户模式下的实现逻辑。就是说相较于运行在IRQL(Interrupt ReQuest Level中断请求)的设备驱动,他们更加有机会被中断(比如翻页、计划)。
 
最后,注意本文的目的,我们忽略特定的Windows特性,如基于某些特定用途的IRQL PASSIVE_LEVEL中断处理和UMDF,通常不是为实时处理而设计的。
 
低延时的敌人
 
为了便于理解,为什么Windows不是合适的实时处理操作系统,我们仔细看一个中断驱动设备的实现,它要在截止时间内在用户模式下处理请求。然后我们一边解释哪些问题会造成不受欢迎的延时。
 
对于一个中断驱动的实时性敏感的设备,Windows能够及时响应请求是很重要的。对于接下来将要讨论的实时处理的敌人,延时可能在实时处理中被引入。逻辑上,在一次中断的处理期间,IRQL降的越低,从ISR到DPC再回到用户处理,越有可能引起长时间和频繁的延时。
 
中断/ISR
 
设备指示需要引起软件注意的方法,是产生一个中断。CPU收到设备发出中断信号后,将该中断注册为一个ISR(Interrput Service Routine)来处理。
 
ISR的执行可能被种因素推迟。CPU可能临时推迟中断,操作系统正在为一个高级的中断服务,临时停用一些可屏蔽的中断,造成整个系统的锁定状态或者其他。同样的,中断可能被硬件因素推迟。遗憾的是,这类中断推迟不能单独通过软件来测量。需要硬件的支持,或者使用总线分析【在现代Intel架构系统中,我们知道硬件延时是非常小的,通常小于1毫秒】。本文中,我们仅讨论中断处理中的软件延时,就是从ISR开始处理到中断被软件处理的期间。
 
因为Windows不允许控制设备中断的优先级,任何ISR执行的时候可以被更高级的中断抢占,除非通过提升IRQL来阻止此类抢占。ISR处理过程中的延时,也可能由其他因素引入,比如处理系统时钟、IPI(Inter Processor Interrupt)例程引起的操作系统中断,或者不受操作系统控制的因素,比如SMI(System Management Interrupt)例程,以及稍后会讨论的其他各种硬件因素。
 
DPC
 
如果该中断的服务需要长时间运行,ISR通常规划一个DPC(Deferred Procedure Call)。DPC在一个较低的IRQL上处理I/O操作,设备和系统中断可以抢占他的执行。如果中断的服务需要以用户模式进行,也需要一个DPC,受IRQL的强加在ISR的限制不允许恢复等待的对象或唤醒用户模式的线程。
 
一般,DPC在请求以后马上在同一个处理器上执行。操作系统为每个逻辑处理器维护一个DPC队列,很有可能其他DPC例程在你的之前运行,当你的处理器上的DPC队列很繁忙的时候,因此增加了延时。
 
因为DPC例程在IRQL的DISPATCH_LEVEL(线程DPC是一个例外,它执行在PASSIVE_LEVEL)执行,处理DPC的过程中任何设备或硬件中断都可能引起延时。
 
CPU收到一个中断开始到DPC例程开始执行的时间间隔,通常表述为“ISR到DPC延时”。如果驱动要求有截止时间,它需要最小化ISR到DPC最大延时。这是个重点,不要错过它:对于实时处理,我们关注的是最大延时,而不是平均延时。ISR到DPC平均延时通常都很好。换句话说,ISR到DPC的最大延时有时候会带来令人吃惊的坏处。这也是经常引入人工产品的地方(如下)。
按规则执行
另一方面,作为一个核心开发者,尽管你的驱动没有任何实时性需求,你也需要关注平稳的执行,避免花太多时间关注IRQL,这样你不会损害你的驱动所在操作系统的实时性。包括花在ISR、DPC以及代码执行中的自循环锁或者通过其他途径提升的IRQL的时间。或者你是一个BIOS/固件开发者,需要更加关注这些。
MSDN对此的建议是,任何DPC或ISR例程都不能执行超过100微秒。实践中,许多驱动违反了此规则,至少在特定的硬件上。如果一个驱动引起了高延时,这并不总意味这是软件的错,许多情形中此类问题可以完全归因于硬件。网络驱动(特别是WIFI),存储端口驱动以及ACPR电池驱动,都是臭名昭著的在中断执行期间引起高延时的驱动。通常在执行实时性任务时,这些驱动要在系统中禁用。
 
用户模式
 
在实时性请求处理模式下,要避免执行用户模式代码。然而有些情况和解决方案依赖于此。当用户模式代码在重要的方式中执行时,显然代码必须执行的越快越好。用户模式代码不应该挂起、等待或者调用任何Windows API库函数。它应该只是用常驻内存,因为硬件页面错误会引发长时间挂起执行线程,等待页面错误处理器通过从地盘读取同步数据来解决它,这通常要消耗数秒钟时间。
 
注意Windows API函数,如VirtualLock只允许向处理器的工作集合执行一个分配,并不为应用程序提供常驻RAM的内存。一个确保应用程序取得常驻内存的做法是,让驱动将用户提供的缓存锁定到内存中。有很多方法可实现它,一些方法比另一些更加复杂。一个简单的方法是,在IOCTL中通过使用METHOD_OUT_DIRECT,由用户应用程序分配缓存,然后提供给驱动作为输出缓存(OutBuffer)。驱动就可以在执行过程中保持这个缓存,及其锁定的内存,直到应用程序退出为止。也有其他更加复杂的方法,在用户模式和核心模式之间锁定共享内存。这些方法的优缺点不在本文的讨论范围内。
 
作为中断执行的一部分的用户模式线程,通常在一个实时性优先级线程池中管理,处于空闲状态被挂起,被从DPC例程中设置的一个调度对象信号唤醒,比如事件或者I/O结束。
 
从处理器收到一个中断到用户模式线程被唤醒后开始执行的时间周期,通常称为“ISR到处理器延时”。包括为该处理器规划一个DPC并执行的时间。在操作系统课程中,这通常被称为“处理器分配延时”。一个部分工作在用户模式的有时间要求的解决方案,必须最小化ISR到处理器延时。
 
当一个实时关键的用户模式的线程要长时间执行的时候,操作系统的规划器通过给用户模式线程一个“超级”实时优先级,来允许它不被抢占,这样它的时间不会过期。包括给处理器分配一个特定调度类设置的任务对象。
 
更多僵尸
 
除了硬件中断、ISR例程、DPC例程、用户模式执行和硬件页面损坏(hard page faults)之外,底层还有其他低延时的敌人不能被忽略,因为他们能引起显著的延时。现在我们来讨论其中一些问题。
 
Inter Processor Interrupts是操作系统启动中断,当一个特定例程在所有逻辑处理器上执行时,会挂起所有设备中断。他们可以由硬件和软件触发。驱动,如ndis.sys利用这种技术,伴随着大量DPC处理,就是启用网络配置的操作系统通常不适合实时处理的原因。
 
SMM(System Management Mode)是处理器的一种专用操作模式,用来处理全系统级别的函数,如电源管理、系统硬件管理或专有的OEM代码。只应该被BIOS或固件接口使用,不能被应用程序或其他操作系统使用。系统管理中断(System Management Interrupt)是在SMM中执行的CPU中断例程,在操作系统之外。SMI的优先级在任何可屏蔽或不可屏蔽的中断之上。因为SMI可以在任何时候中断任何处理,它需要被尽可能快的执行,否则在执行实时性任务时将造成全系统不可用。
 
一个启用变速设定的CPU内核,在临时设置为“stop clock mode”若干毫秒之后可以达到很高的温度,需要降温。这个处理器上的中断将被挂起,直到CPU可以再次运行为止。
 
CPU的设计缺陷也会导致不可解释的CPU宕机。现代CPU中的一些CPU缺陷,和处理器的C-states(实现CPU空闲)以及P-state(实现CPU变速)相关,可以无限期冻结一个处理器,直到满足某些条件为止。CPU制造商发布包含这些信息的勘误表(规格更新)。
 
上面提到的许多引起延时的因素,不是可以由开发者在软件层面控制的,除非修改系统配置。然而通过软件衡量操作系统的实时性能力是可行的,这样终端用户可以检查他的系统是否适用。
 
测量延时
 
我们列出了许多潜在的危险,对于一个有实时性要求的驱动。如果一个驱动要支持一个现成的有实时性需求的硬件在任意系统上运行,基本不会成功。所以在安装或购置之前测试一个系统的能力,以免失败和引起客户不满,或者通过帮助终端用户配置系统以克服运行你的解决方案的障碍,是很有帮助的。
 
有一些工具让你可以测量ISR和DPC时间。其中一个是XPERF,是可选安装的Windows性能工具包的一部分。Windows性能工具包,包含在Windows WDK和SDK中。关于如何使用XPERF,点击(Get Low - Collecting Detailed Performance Data With Xperf
 
另一个工具是LatencyMon,除了ISR和DPC时间之外,还提供硬件页面错误(hard page faults)。此外还提供不同的延时测量工具,包括ISR to DPC以及ISR to user process延时。LatencyMon是本文作者写的。
 
作为驱动代码的作者,通过使用KeQueryPerformanceCounter函数自己添加测量点是微不足道的。要测量你DPC例程的执行时间,你可以简单的在例程的开始和结束查询性能计数,比较之间的差异即可。改方法同样也可以测量ISR to DPC和ISR to user的执行延时。
 
以前使用的一种测量DPC延时的技术是,安装一个内核周期计时器,来测量精确中断的间隔。Windows的计时器是基于软件的,依赖于时钟中断的精度。操作系统的时钟是全局资源,一些软件组件和应用程序会竞争它,只有低时钟周期的请求会胜出(有最低要求的应用程序胜出)。因为Windows的计时器没有直接硬件支持,就是说它们不是非常精确。在Window8更加如此,因为引入了新特性“动态时钟节拍”,系统时钟不再在确定的间隔后中断,而是在操作系统认为必要的时候,基于节省电量的原因。这使得任何依赖内核计时器的测量方法都不可靠。如果一个设备需要在软件中以精确的时钟访问,硬件需要自带能触发中断的计时器,使得软件可以及时的提供服务。这就是事实,尽管Windows8.1有一个新特性叫做高精度计时器。
 
从WIndows Vista开始,Windows提供一系列函数和类,来检索操作系统内核收集的事件跟踪信息。其中,这让你可以获得系统中执行的ISR和DPC的信息,以及硬件分页错误。遗憾的是,这些类不支持你收集高级IRQL的时间消耗,基于ISR和DPC之外的原因,比如运行在自旋锁和IPI的代码。
 
一个测量SMI最大执行时间和无法解释的处理器停顿的方法,是测量在ISRQL的HIGH_LEVEL轮询的紧密循环的最高级中断。这个投机方法然而,不会测量软件发起的SMI例程的执行。
 
同样的,你可以测量IPI的执行时间,通过在低于IPI_LEVEL的较低的IRQL执行循环。需要考虑的一件事是,有些版本的操作系统可以使用一种叫做“lazy IRQL”的IRQL管理技术。在这种技术下,操作系统存储的IRQL值,不总是和处理器的任务优先级状态对应。
 
中断绑定在处理器上。Windows允许完整配置使用你系统中的哪个处理器来执行线程,通过设置偏好,而几乎没有哪个处理器处理设备中断的控制。执行关联特定设备的ISR的处理器,和硬件最为相关,因此Windows将BIOS设置用于中断偏好。有的芯片组让中断在所有的处理器上传播,另一些仅仅让中断在CPU 0上执行。这使得运行在特定硬件上的软件通过偏好选择哪个处理器来运行,是没用的。这种情况不同于你可以提供你自己的操作系统的情况,我们稍后将讨论。GetProcessorSystemCycleTime API函数是一种简单快速的方法,找出系统的哪个处理器在处理中断和DPC。
 
硬件控制
 
前面提到的,如果你在为一个设备或解决方案开发实时性驱动,可能部署在某些系统上不能满足要求,因为特定的系统配置。
 
如果你有幸在你的解决方案运行的系统上有丰富的控制选项,那你有可能拥有由你支配的选项来避免失败。对于哪些使用现成产品的开发者,这些选项是不开放的。依赖于你的解决方案的截止时间和市场需求,选择一个特定的操作系统配置能让你提交一个能确保工作的解决方案。
 
一旦操作系统的配置完全在你的掌控之下,你就有了添加额外的硬件来支持你处理延时敏感任务的机会。一个选项在硬件层面实现实时逻辑,比如使用FPGA或者DSP开发板。也有WIndowns的实时(软件)扩展,允许你获得实时性而不需要额外的硬件,比如IntervalZero RTXTenasys Intime。这些解决方案通过伴随Windows运行一个独立的操作系统,负责实时逻辑的处理。
 
但是尽管没有外来硬件或者实时性扩展,仍然有很多可以做,在你控制整个操作系统的情况下。如果你的解决方案要求的响应时间(或精确性)不是非常高,比如超过10毫秒,那你通过配置已知的硬件和驱动不会带来高延时的Windows操作系统,可以提交一个Windows上运行的解决方案,也可以满足截止时间的要求。
 
通过仔细的选择配置中的母版晶片组和驱动,可以控制哪个处理器连接到中断,允许你通过偏好为实时性关键任务预留一个或多个处理器,可有效避免ISR和DPC带来的延时。配置哪个CPU处理ISR、DPC和线程,可以更进一步通过特定的处理器组参数来启动操作系统,经过测试以后。
 
自定义配置的一个重要部分是电源管理。就像前面讨论的,一些CPU和BIOS的电源管理特性可造成实时性处理的延时。若可能,禁用这些特性,可避免实时处理中的不当延时。
 
音频行业的用户做了很多研究,在如何配置他们的Windows工作站上,以获得低延时,所以网上有很多信息。推荐使用DAW(Digital Audio Workstation)关键词。当然也有计算机生产商,定制裁剪和提供桌面电脑和笔记本,以在Windows上处理低延时任务。
 
总结
 
你可以看到,使得Windows系统能够处理实时任务,需要一些测量、解惑,甚至考虑一些不特定的因素。在为你的解决方案配置和测试一个系统之后,你可能仍然觉得不舒服,在给你的客户提供保证方案经得起测试和分析的时候。
 
记住:Windows不是RTOS。如果你的解决方案需要Windows确保的响应时间,你可能大部分时候,需要你的解决方案依赖终端用户的配置。问题就转变为,多少情况下你的实时性需求不能被满足,不满足这些需求的结果是什么,是否有任何裁剪或者系统配置需要执行来提高结果。希望这篇文章解释了问题,如何客服一些问题,以及有哪些资源可用,对于需要在Miscosoft Windows下交付一个实时性敏感的解决方案的核心开发者。
 
原文地址:https://www.cnblogs.com/wuchaochao/p/7760749.html