中断服务程序 ISR

硬件中断处理是实时系统的关键,因为它是外部时间通知系统的方式。 ISR亦称为中断处理函数,是对中断的正确响应。可以使用任何ISR连接到任何没有被VxWorks使用的中断上。当关联的中断发生时,VxWorks运行ISR;ISR的处理不会延迟,除非你配置系统去延迟。

VxWorks的ISR配置

默认支持ISR。但是,中断栈和额外特征可以被配置。此外,ISR的延迟支持和显示函数支持可以被增加到系统。

配置中断栈

所有中断使用相同的中断栈。栈在系统启动时更加配置参数来分配和初始化。它必须足够大来处理最糟糕的嵌套中断的情况。中断栈的大小由ISR_STACK_SIZE参数指定。

注意:一些架构并不支持单独的中断栈,ISR使用被中断的任务的栈。这种架构中,确保创建的任务带有足够的栈空间来处理中断嵌套或普通嵌套的情况。架构是否支持单独中断栈请看BSP。

中断栈填充

默认,中断(和任务)栈一0XEE填充。栈填充在调试中使用checkStack()非常有用。对于开发系统来说,不填充比起填充系能更加。使用VX_GLOBAL_NO_STACK_FILL来启用栈填充。

中断栈保护

系统通过配置INCLUDE_PROTECT_INTERRUPT_STACK组件来提供从栈顶端到底端区域的保护,如果MMU被启用。当任务超出了栈的预定义大小时,将产生溢出,这能保证数据完整性,不会破坏其它栈和数据。对于向下增长的架构,可以防止缓存溢出损坏栈上面的内存。当任务企图访问任何保护区域,CPU将产生异常。当包含区域被插入时,它的大小总是被圆整为MMU页的整数倍。

包含区域大小通过下面的参数来定义:

  • INTERRUPT_STACK_OVERFLOW_SIZE -- 中断栈的溢出大小
  • INTERRUPT_STACK_UNDERFLOW_SIZE -- 中断栈的下溢大小

如果系统的尺寸有问题,本组件可以在最终阶段或生成阶段移除。设置参数为0来阻止响应的保护区的插入。

增加显示函数支持

为了增加isrShow函数需要配置INCLUDE_ISR_SHOW组件。

编写ISR

编写ISR的基本原则是仅可能短。耗时的活动不应该放在ISR中。

ISR上的限制

ISR程序不应调用会引起调用者阻塞的函数或使用浮点协处理器的函数;对C++的使用也有限制;不能访问共享数据区域。

无阻塞的函数

大多函数可以在ISR中使用,但是有一些重要的限制。这些现在阻止ISR不能运行在普通的任务上下文中并且没有任务控制块(TCB),所以所有ISR应该共享一个栈。

由于这个原因,ISR的基本限制是必须不能调用会引起阻塞的函数,因为如果阻塞,内核将切换上下文。因此,不应该尝试获取信号量,因为信号量不可用时,将阻塞ISR,但是可以在ISR中释放信号量。因为内存分配malloc和释放free函数获取了信号量,所以也不能被调用。举个例子,ISR不能调用任何创建和删除函数。

ISR也不能通过驱动执行I/O操作。尽管对I/O系统没有固有的限制,大多数设备驱动要求任务上下文,因为它们可能等待设备而阻塞调用者。一个重要的例外是管道驱动,它被设计成可以在ISR中使用。另外也提供了几个函数可以在ISR中使用来打印消息到系统控制台:logMsg,kprintf和kputs。

无浮点协处理器函数

所有ISR不能调用使用浮点协处理器的函数。被intConnect创建的中断驱动代码不保存和恢复浮点指令。如果ISR需要浮点指令,必须显式地使用fppArchLib的函数来保存和恢复浮点运算协处理器的寄存器。

C++代码限制

当使用C++编写ISR或以C或汇编编写的ISR调用C++函数时,需要特别小心。C++会引入以下问题:

  • intConnect函数要求中断函数的地址,但是非静态成员函数不能使用,因此必须实现静态成员函数。
  • 在ISR中不能实例化和删除对象。
  • ISR中执行的C++代码必须严格限制嵌入式C++。没有异常和RTTI可以被使用。

不能直接访问共享数据区域

所有ISR不应该设计成直接访问共享数据区域。ISR继承了被它抢占的任务的内存上下文,如果认为恰好发生在没有包含共享数据区域的内存上下文(内核或进程)中,它无法访问这块内存,将导致异常。为了可靠的访问共享内存区域,ISR必须利用已经被在包含共享区域中的内存上下文(如附加到共享区域的进程)中创建的任务(ISR不能创建任务本身)。那么,在ISR终止之后,任务能在该区域执行想要的操作。

ISR提供的能力

所有ISR的工具类库,如linked-list和ring-buffer类库,能被ISR使用。全局的errno变量通常被intConnect作为中断进入和退出的一部分来保存和恢复。因此,errno能被ISR引用和修改。所有可被调用的函数参考内核开发指导。

中断任务通信

中断事件通常传播到任务层代码。很多VxWorks特性在中断层不可用,包括对于设备的I/O操作,不是管道。然后,可以使用以下技术在ISR和任务层代码之间的通信:

  • 共享内存和环形缓存 -- ISR能与任务层代码共享变量,缓存和环形缓存。
  • 信号量 -- ISR能够释放信号量给正在等待的任务
  • 消息队列 -- ISR能发送消息到消息队列,让任务去接收(除了使用VxMP的共享消息队列)。如果消息队列已满,则消息被丢弃。
  • 管道 -- ISR能写管道,任务可以读取。任务和ISR也可以写同一管道。然而,如果管道已满,则消息被丢弃。ISR不能调用除了write以外的任何函数。
  • Signal -- ISR能够发信号给任务,引起信号处理器的异步调度。
  • VxWorks事件 -- ISR能发送事件给任务。

保留高中断级别

前面描述的中断支持对于大部分应用程序是可接受。但是,在某些情况,需要对事件的低层控制,如关键的运动控制和系统的故障响应。对于这种情况,需要预留最高等级的中断级别来确保0延迟的响应时间。为了完成0延迟响应,VxWorks提供了intLockLevelSet()函数,来为特定的级别设置系统级的中断锁定时间。如果没有指定级别,则默认是处理器架构支持的最高级别。

注意:某些硬件阻止了屏蔽某种中断级别;检查硬件手册。

高中断级别ISR的限制

ISR连接到没有被锁定的中断级别(要么中断级别高于intLockLevelSet设置的级别,或定义在硬件中的作为非屏蔽的级别)有特殊的限制:

  • ISR只能使用intVecSet连接
  • ISR不能使用任何依赖于中断锁的操作系统能力来正确操作。最终结果是ISR不能调用任何其它VxWorks函数,处理reboot。

警告:NMI用于任何VxWorks功能除了reboot之外都是不推荐的。函数被标为中断安全,并不意味着NMI也安全。

系统时钟的ISR修改

在引导时系统初始化期间,系统时钟ISR--usrClock()--被附加到系统时钟中断。每次系统时钟中断,usrClock被调用来更新系统的tick计数器和运行调度。可以在usrClock中添加应用程序相关的代码。然而,必须记住它是在中断上下文中被执行,所以只有那些被限制的函数可以被调用。

如果使用了低功耗管理,则允许处理器睡眠多个tick。usrClock及tickAnnounce在处理器睡眠时不被调用。相反,usrClock在处理器被唤醒之后,如果至少过了1个tick,它只被调用一次。在usrClock中的应用程序代码应该检查tick的计数器,以免设置处理器为睡眠模式而丢失时间。

关联ISR到中断

处理被VxWorks使用之外的,还可以使用硬件中断。intConnect允许C函数连接到任何中断。该函数的参数是中断在中断向量表中的偏移,和要连接到中断的C函数地址及传递给函数的参数。当中断发生时,连接的C函数将被使用指定参数进行调用。该函数称为ISR。

对于VME的板子,BSP提供了2个标准函数来控制VME总线中断,sysIntEnable和sysIntDisable。

注意:intLock和intUnlock用于UP配置的VxWorks,对于SMP配置的VxWorks不适用。SMP中的几个替代方法是包括ISR可调用的自旋锁(spinlock)。

ISR信息

每一个ISR都有内核对象。ISR提供了管理信息。

INCLUDE_ISR_SHOW组件提供了isrShow;

INCLUDE_ISR_OBJECT组件提供了isrLib;

调试ISR
I/O使用的限制

通常ISR不能执行I/O,因为它们会引起阻塞。这意味着你不能调用标准的I/O操作如printf和puts。下面的方式可以用来调试:

  • 编写一个ISR使用全局变量并用相关的数据更新。shell能够在运行时显示该变量的值。
  • 使用logMsg,kprintf和kputs来打印消息到控制台。

使用全局变量的好处简单和对性能影响较小。缺点是如果目标系统挂掉,shell无法显示该变量值。

使用logMsg的好处是它能自动打印消息到控制台,它也比kprintf或kputs对系统性能影响小。确定是在调用它之后,目标系统会短暂挂起,消息字符串可能不显示在控制台。原因是因为logMsg内部的异步操作,它首先写消息队列,然后在记录任务包消息并送到控制台。

kprintf和kputs的好处是它们同步输出消息(使用轮询模式),它可以指示ISR执行有多块。(也用于内核引导顺序和任务切换的钩子)。缺点是对系统性能影响较大,因为它们从串口输出消息。

使用全局变量,除了用于输出调试信息以外,还可以改变ISR的流控制。

栈大小和溢出

在开发阶段使用checkStack来查看使用了多少空间。

中断级别的异常

当任务引起异常时,认为被挂起,系统其余部分继续运行。然而,当ISR引起异常时,没有安全额办法来处理异常。ISR没有上下文可以被挂起。相反,VxWorks存储在内存地抵制的特定位置保存了异常的描述,并执行重启。

VxWorks的bootloader在启动时检测低内存地址中是否有异常描述,如果有,显示在系统控制台中。引导程序的e命令可以重新显示异常信息。

ISR和内核工作队列溢出

VxWorks通过使用工作延迟机制而不是锁定中断关键部分来保护临界区部分来把中断潜伏期减少到最小。