Windows CE系统机制中的调度

dnsRRR 2009-06-19 01:02:14
Windows CE的陷阱调度是嵌入式系统开发的一个重要内容,陷阱调度的好坏关系到系统的健壮性和稳定度。陷阱调度看起来只有几个常用语句,故有些开发人员常常会对陷阱调度轻视和在使用上思路模糊。

  近期我在一个嵌入式开发项目中就体验到轻视陷阱调度的惨痛教训,因为对陷阱调度没有处理好,后果是严重影响系统稳定性。因此,我认为陷阱调度并不是表面看起来的那么简单。本文分享在此项目过程中对陷阱调度的一些看法。

  一.什么是陷阱调度机制?

  一般来说,嵌入式操作系统主要由两部分组成:运行在核心态的内核系统和运行在用户态的环境子系统组成。因此,Windows CE系统被划分为两层:执行体和内核。而内核始终运行在核心态下,除了中断服务例程(Interrupt Service Routine,ISR),正在运行的线程是不能抢先内核的。为此,Windows CE为执行体、内核、设备驱动程序等核心态提供了一些基础系统机制。

  (1)Windows CE系统机制

  Windows CE系统机制包括陷阱调度、执行体对象管理器、各种同步对象以及本地过程调用等。一般来说,可以分成两种对象类型:执行体对象和内核对象。内核以内核对象的形式给执行体提供其它的同步机构,称为“调度程序对象”。包括进程、线程、事件、信号量、互斥体、可等待的定时器、文件等同步对象。每个同步对象有两种状态:“有信号”,“无信号”。内核还提供一组严格定义的、可预测的、使操作系统得以工作的基础设施,这为执行体的高级组件提供了必须的低级功能接口。内核除了执行线程调度外,几乎将所有的策略制定留给了执行体。同时,Windows CE运行中的CPU 会支持两个级别的权限,其中较高级别的权限称为内核态,较低级别的权限称为用户态。

(2)陷阱调度(Trap Dispatching)机制

  Windows CE的基本机制之一是陷阱调度,属于内核功能。包括中断调度、延迟过程调用(DPC)、异步过程调用(APC)、异常调度、系统服务调度。

  陷阱处理程序是Windows CE用来处理意外事件的硬件机制。当异常或中断发生时,硬件或软件就能检测到它们,并捕获正在执行的线程,CPU会从用户态切换到核心态,将暂停正在处理的事情,把控制转交给内核的陷阱处理程序。同时,该模块还能检测异常和中断的类型,并将控制交给处理相应情况的代码。因此,陷阱调度机制是当异常或者中断发生时,能够保存当前线程状态并转向相应处理的一种系统机制。

  在Windows CE系统里,内核通过以下方式来分辨中断和异常:中断是一个异步事件(可以在任何时间产生),不管处理器在执行什么程序。典型的中断由I/O设备、时钟、定时器产生,必要时可以屏蔽中断。而异常是一个同步事件,它是由正在执行的特定代码产生的,重新执行相同的代码会重复产生特定的异常。比如访问非法内存、除数为0等。系统把系统服务也作为异常来处理。

  二.陷阱调度核心:中断机制

  在Windows CE陷阱调度中最重要之一是中断机制。当陷阱处理程序被调用时,将在记录机器状态时暂时禁用中断,它会创建一个陷阱帧(Trap Frame)来保存被中断线程运行现场,并在合适的时候恢复线程执行时使用。陷阱帧通常是完整的线程描述表的子集。

  (1)中断调度

  不同的CPU中断机制是不一样的,Windows CE的中断调度程序会将硬件中断级映射到由操作系统识别的中断请求级别(Interrupt ReQuest Level,IRQL)的标准集上。这与线程的调度优先级是完全不同的含义,调度优先级是线程的属性,而IRQL则是中断源的属性。因此,每个CPU都具有一个IRQL设置,其值随着操作系统代码的执行而改变。内核定义了一组可移植的IRQL,如果CPU具有与中断相关的特性,则可以增加IRQL。IRQL按优先级排列中断,并进行中断服务,较高优先级的中断服务可以抢占较低优先级的中断服务。

 一般来说,IRQL从高往低到设备都是为硬件中断保留,而DPC和APC级中断是内核和设备驱动器产生的软件中断。低优先级(也称作被动级)实际上并不是真正的中断级,在该级上执行的是普通线程,并允许发生所有的中断。IRQL设置决定了每个处理器可以接收的中断。当核心态线程运行时,可以提高或降低处理器的IRQL来屏蔽一些事件。

  如果中断源的IRQL高于当前中断设置,则中断可以中断该处理器;如果中断源的IRQL等于或低于当前中断设置,则中断将被封锁或“屏蔽”,直到一个正在执行的线程降低了IRQL。当产生中断时,陷阱处理程序能提高处理器的IRQL直到与中断源所指定的IRQL相同,这可以保证服务于该中断的处理器不会被同级或较低级的中断抢先。被屏蔽的中断将被另一个处理器处理或阻挡,直到IRQL降低。因为改变处理器的IRQL对操作系统具有如此重要的影响,所以它只能在核心态下改变。

  (2)硬件中断

  最典型的硬件中断是由I/O设备产生的,当这些设备需要服务时,必须通知处理器。中断驱动的设备允许操作系统通过将指令执行与I/O操作重叠进行,以获得处理器的最大利用率。处理器启动发往设备的I/O传送或来自设备的I/O传送,然后在设备完成传送时执行其它线程。当设备执行完后,中断处理器就能获得服务。定点设备、打印机、键盘、磁盘驱动器以及网卡通常都是中断驱动的。

  大多数硬件中断允许设备驱动程序注册其设备的ISR,包含内核所需的设备ISR与中断特定级相联系的所有信息,包括ISR的地址、设备中断的IRQL以及与ISR相联系的内核入口。当中断对象被初始化后,称为调度代码的一些汇编语言代码指令就会被存储在对象中。当中断发生时,这些代码会调用真正的中断调度程序,并传递一个指向中断对象的指针。中断对象包含了第二个调度程序例程所需要的信息,以便定位和正确地调用设备驱动程序提供的ISR。需要两步过程的原因是自硬件完成初始调度后,没有方法可以在初始调度上传递一个指向中断对象的指针。

(3)软件中断

  软件也可以产生中断,包括:启动线程调度、处理定时器到时、在特定线程的描述表中异步执行一个过程,以及支持异步I/O操作等。例如,内核可以发布启动线程调度的软件中断,内核也可以禁用中断以使处理器不被中断,但这种情况很少出现,只在处理中断或调度异常的关键时刻才这样做。软件中断由中断调度程序的子模块响应,它确定中断源并将控制转交给处理中断的外部例程(ISR),或转交给响应中断的内核例程。

  三.Windows CE如何进行中断处理?

  (1)Windows CE中断处理流程

  ①硬件设备向Kernel发送中断异常的代码,如果检测到这个中断异常,就会被Kernel层的异常处理所截获。然后,中断服务调度程序会调用OAL例程中的OEMInterruptDisable函数,这个函数会通知硬件在处理完这一中断前关闭特殊的中断,但其它的中断仍然处于开放状态,中断服务例程ISR会被调用来决定如何处理这一中断。

  ②Kernel接收到ISR的返回值可得知如何处理这一中断。它的响应结果之一是忽略掉这一中断不作处理(SYSINTR_NOP),或另一结果是准备执行IST。然后,Kernel引发中断服务调度程序去唤醒中断服务线程。IST是常规的Win32线程,一旦启动后,它会创建必要的EVENT,然后等待该EVENT被激发。中断服务调度通过调用PulseEvent函数来激发EVENT,从而唤醒IST线程运行。当唤醒以后,IST会对中断进行处理。

  ③当IST处理完成后,还需要调用InterruptDone函数通知Kernel。Kernel则调用OEMInterruptDone函数完成此次中断的处理过程。最后,OAL例程通知硬件设备重新启用中断。

  (2)中断处理涉及的几个常用函数

  Windows CE在处理中断时会涉及到两类函数的使用,第一类是供OAL调用的ISR函数,例如HookInterrupt函数在OEMInit函数中被调用以关联IRQ和ISR,UnhookInterrupt函数用来终止IRQ和ISR的关联。第二类是供驱动程序调用的IST函数,例如InterruptInitialize函数用来将EVENT对象和逻辑中断号关联并允许中断,InterruptDone函数用来通知中断处理的结束,InterruptDisable函数被驱动程序调用以关闭中断同时取消被InterruptInitialize初始化的EVENT对象。


  ISR函数属于OAL层,它将CPU寄存器中的数据移动到内存缓冲区中,但是它不能做更多的工作,其中一个原因是它不能访问用户态的存储区,它要把这些工作交给IST来完成。ISR函数做的另一项工作是进行物理中断号和逻辑中断号的映射。经过ISR后,就能把这一物理中断转换成Windows CE标准的SYSINTR_KEYBOARD逻辑中断。Kernel会根据这个逻辑中断值找到对应的EVENT,从而唤醒IST。

  最后,需要提醒一下的是,常常有一些开发人员习惯拖延或忽视陷阱调度的编写。因为轻视陷阱调度这一坏习惯是如此常见,它甚至已经影响到了Windows CE系统的研发。因此,建议从开始时就应该着手进行陷阱调度研发和计划,应该投入大精力把陷阱调度的策略融合到嵌入式产品中。

...全文
516 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
daifeijin 2009-06-19
  • 打赏
  • 举报
回复
呵呵……
dnsRRR 2009-06-19
  • 打赏
  • 举报
回复
断调度
硬件中断一般来自那些要通知处理器它们需要服务的I/O设备。中断控制设备使操作系统得以通过重叠主进程及I/O操作最大限度地利用处理器。某线程启动了 一个对某设备的I/O传送后即可在设备完成传送的过程中继续其他的工作。设备完成任务后,会中断处理器以要求服务。指点设备、打印机、键盘、磁盘驱动,以 及网卡一般来说都是中断控制的。
系统软件也可产生中断。例如,内核可以发出一个软件中断开始线程调度或异步地打断线程的执行。内核能够禁用中断使处理器不被打断,但它只在少数情况下这么做——比如正在处理一个中断或者调度一个异常的时候。
内核装载中断陷阱处理程序以响应设备中断。中断陷阱处理程序要么将控制传送给处理该中断的外部例程,要么传送给响应该中断的内核例程。设备驱动提供 ISRs(interrupt service routine)服务于设备中断,而内核为其他类型的中断提供中断处理例程。

硬件中断处理
在Windows支持的硬件平台上,外部的I/O中断从中断控制器的一个引脚进入。接着中断控制器从一个专门的引脚向处理器发出中断请求。一旦处理器被中 断,它就会向控制器询问中断请求(IRQ)。中断控制器将中断请求翻译成中断号,此号码被作为一个中断调度表(interrupt dispatch table,IDT)的索引值使用,借此将控制转到适当的中断调度例程。系统启动时,Windows把处理所有中断和异常的内核例程的指针填入IDT中。

//EXPERIMENT: Viewing the IDT

Windows将硬件中断请求映射到IDT中的中断号,系统也使用IDT为异常配置陷阱处理程序。例如,x86和x64的缺页(当某线程试图访问一个不存 在或为被定义的虚页)的异常码为0xe。因此,IDT中的0xe项指向系统的页错误处理程序。虽然Windows支持的体系结构允许IDT拥有256项, 但特定机器能负担的IRQs数量取决于该机器所使用的中断控制器的设计。
每个处理器拥有一个独立的IDT,即如果有需要不同的处理器可以执行不同的ISRs。例如,在一个多处理器的系统中,所有的处理器都接到了时钟中断,但只 有一个处理器响应这个中断去更新系统时间。而所有的处理器都回用到中断来测量线程的时间片并且在某个线程的时间片用完后重新调度。同样地,某些系统配置会 要求一个专门的处理器来处理特定的设备中断。

x86中断控制器
大多数x86系统依赖i8259A可编程中断控制器(Programmable Interrupt Controller,PIC)或者i82489高级可编程中断控制器(Advanced Programmable Interrupt Controller,APIC)的变种;大部分新出的计算机都包含了一个APIC。PIC标准源自早期的IBM PC。PICs只工作在单处理器系统中,有15个中断引脚。APICs和SAPICs被用于多处理器系统,有256个中断引脚。Intel和其他公司定义 了多处理器规范,这是一个以APIC为核心的x86多处理器系统设计标准。为了和单处理器系统以及在单处理器模式下启动多处理器系统的引导代码兼容, APICs支持一个PIC兼容模式,该模式只应付15个中断并且只向主处理器传递中断。APIC实际上是由几部分构成的:一个I/O APIC,它负责从设备接收中断;一个本地APIC,它负责接收I/O APIC通过一个私有的APIC总线传来的中断,接着再中断与之相连的CPU;还有一个类似i8259A的中断控制器,它将APIC的输入翻译成PIC的 等价信号。I/O APIC负责实现中断路由算法——这是可以用软件改变的(硬件抽象层(hardware abstraction layer,HAL)为Windows提供了这种选择)——既在处理器间平衡了对设备中断的受理,又尽量利用位置优势,将设备中断传送给刚刚处理了相同类 型中断的处理器。

x64中断控制器
x64体系结构是与x86操作系统兼容的,所以x64系统必须提供与x86相同的中断控制器。而一个大的差异是,x64的Windows必定包含APIC,并且由APIC负责中断控制。

IA64中断控制器
IA64体系结构依赖于SAPIC(),它是APIC的升级版。APIC和SAPIC体系结构间的主要差异是APIC系统中的I/O APICs是通过一个私有APIC总线向本地APIC传送中断,而在SAPIC系统中为求速度中断是通过I/O及系统总线传送的。另一个差别是在APIC 系统中,中断路由及负载均衡是由APIC总线负责,而SAPIC系统中并没有一个私有的APIC总线,就要求将这些功能编入固件中。虽然负载均衡和路由功 能已存在于固件中,Windows并未加以利用;它选择以轮转方式将中断静态地分配给处理器。

//EXPERIMENT: Viewing the PIC and APIC

软件中断请求级别(Software Interrupt Request Levels ,IRQLs)
虽然中断控制器做了中断级别的优化工作,Windows还是强制使用自己的中断优先级方案,即IRQLs。内核在内部以数字表示IRQLs,x86上是从 0到31,x64和IA64上是从0到15,数字越高则中断的优先级别越高。内核为软件中断定义了一系列标准的IRQLs,HAL也将硬件诊断号映射到 IRQLs。



中断是根据优先级被响应的,一个高优先级的中断会比一个低优先级的中断先辈响应。当有一个高优先级的中断产生,处理器保存被中断线程的状态并呼叫与之相关 的陷阱调度器(trap dispatchers)。陷阱调度器提升IRQL并调用该中断的服务例程。在中断例程结束之后,陷阱调度器降低处理器的IRQL到中断发生前的级别,接 着载入之前存储的机器状态。被中断的线程从被中断的点重新开始执行。当内核降低IRQL后,之前被屏蔽的优先级较低的中断就可能被接收。如果顺利的话,内 核会重复这一过程来处理新的中断。
IRQL的优先级与线程调度的优先级完全不同。后者是线程的一个属性,而IRQL是中断源,比如键盘、鼠标的一个属性。此外,每个处理器有一个IRQL设置,该设置在执行系统代码时发生变化。
每个处理器的IRQL设置决定了该处理器可接收的中断。IRQLs也被用来同步访问内核模式的数据结构。当一个线程在内核模式下运行,它既可以直接调用 KeRaiseIrql和KeLowerIrql来提升或降低处理器的IRQL,也可以间接地通过调用函数获得内核同步物件以提升或降低处理器的 IRQL。从拥有高于当前IRQL的中断源发出的中断可以中断处理器,而从有着小于或等于当前IRQL的中断源发出的中断会被屏蔽,直到有执行线程降低该 IRQL。
访问PIC是一个相对费时的操作,于是使用PIC的HALs做了个性能优化,叫做lazy IRQL,来避免PIC访问。如果IRQL杯提升,HAL将内部记录这个新的IRQL而不去改变中断掩码。如果接下来 有一个低优先级的中断发生,HAL就设置中断掩码以适应第一个中断并且推迟低优先级的中断直至IRQL被降低。因此,如果在IRQL升高时没有低优先级的 中断发生,HAL就不必修改PIC。
一个内核模式的线程提升或降低运行它的处理器的IRQL取决于它的任务。例如,如果一个中断发生,陷阱处理程序(或者是处理器)会提升处理器的IRQL到 为该中断源指定的IRQL。这个高度屏蔽了所有小于或等于该IRQL的中断(只作用于该处理器),保证了在处理器响应该中断的过程中不会被同级或级别较低 的中断打断。被屏蔽的中断要么被交由其它的处理器处理,要么就要等到IRQL降下来。因此,系统中的所有组件,包括内核和设备驱动都希望IRQL能够待在 无源级别(也叫作低级别)。原因是如果IRQL不被长时间的置于较高的状态,设备驱动就可以及时地响应硬件中断。

19,504

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 嵌入开发(WinCE)
社区管理员
  • 嵌入开发(WinCE)社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧