关于中断处理的系列问题
中断的任务被调度后,再度获得运行时是从任务被中断处执行。
中断的任务被调度后,再度获得运行时是从任务被中断处执行。
在中断ISR中调用OSIntExit(), 在OSIntExit()中又调用OSIntCtxSW(),此时堆栈结构如下:
栈顶-->OSIntCtxSW()返回地址 2字节
OSIntExit()返回地址 2字节
SP-4-->保存寄存器PUSHALL
中断断点(任务返回地址)
。。。
可见SP-4就是寄存器信息和中断返回地址,OSCtxSw()弹栈后就回到了任务断点。
KEIL调用函数时压入堆栈的就是2字节的返回地址。
中断剩余部分没有被跳过,准确的说是被OSCtxSw借用了。它的内容只有POPALL和RETI,此处没有错误。
中断完成后有可能使某些资源满足,导致阻塞任务就绪,因此要进行任务调度,这样中断返回后的任务就不一定是这个被中断的任务了。
***************************************************************************************************
在UCOS 中典型的中断处理被建议成如下:
void OSTickISR(void)
{
保存处理器寄存器的值;
调用OSIntEnter()或是将OSIntNesting加1;
调用OSTimeTick();
调用OSIntExit();
恢复处理器寄存器的值;
执行中断返回指令;
}
你的程序也是这么做的:
OSTickISR:
USING 0
PUSHALL
CLR TR0
MOV TH0,#70H ;定义Tick=50次/秒(即0.02秒/次)
MOV TL0,#00H ;OS_CPU_C.C 和 OS_TICKS_PER_SEC
SETB TR0
LCALL _?OSIntEnter
LCALL _?OSTimeTick
LCALL _?OSIntExit
POPALL
RETI
这样好象有问题,我觉得“ 调用OSIntEnter()或是将OSIntNesting加1;”应放在中断的第一条语句来执行,以保证在被更高级别中断打断之前将OSIntNesting加1,以避免在高级别中断中发生任务切换。
如果在“保存处理器寄存器的值”时被打断;发生任务切换,何时再返回到被打断的中断程序很难讲了,从切换的原理,好象当被中断打断了的任务再次运行时,应是从被高级别中断打断的低级别中断程序里面的断点处继续运行,即回到中断程序把其余下的部分执行完,再返回任务。( 这样理解请杨大师仔细批评指正一下), 这样,中断处理的时间太长了。中断失去了意义。
对!“ 调用OSIntEnter()或是将OSIntNesting加1;”应放在中断的第一条语句来执行。我当时没有考虑清楚,这是又一个BUG。
你的理解正确。
中断处理延迟在OS里是很常见的现象,最大中断处理延迟是评价OS性能的重要指标。这说明CPU的负荷能力是有限的,工程上以最大中断处理延迟为指标选择CPU速度,只要延迟在允许的范围内就可以接受。如果“中断处理的时间太长了,中断失去了意义”,那么说明所选CPU的能力不够。
在理解OS工作原理的时候,不要想得太理想化,否则总是不得要领,你的思维定势会误导你的思考。注意计算机是个离散数字系统,它不能连续运行。有些观念应该确立,例如:单CPU系统在微观上串行,在宏观上并发;实时指在一定时间范围内完成任务;中断处理必然有延迟等等。
不是你没考虑清楚,而是UCOS 邵贝贝翻译的UCOS 书里也是这么写的,不知UCOS 源码是依据此而产生一些BUGS?
而且您设计的调度方案,当程序任务再度运行时,是直接返回到任务。而不是返回到中断里, 所以当任务再度运行时是回到任务去的,中断剩余部分就被跳过去了。如果中断剩余部分有重要的内容,那就有BUG了。
这样理解对不对呢?
uC/OS-II一进入中断,理论上应立即原语执行OSIntNesting+1,以便后续中断了解中断嵌套情况,不至于在中断嵌套里切换任务。我认为原作此处是个BUG。有些CPU要求中断后必须至少执行一条指令,这期间不会嵌套中断,那么这条指令就可以是OSIntNesting+1原语操作或者关中断(以便原语操作),这样中断嵌套就被严密监视了,不会漏掉导致判断错误。
任务切换是通过模拟一次中断实现的,它和硬件中断产生的堆栈样式相同。在中断嵌套里不会执行任务切换,所有嵌套中断都使用被中断任务的堆栈空间,在最后一个中断快要退出时进行任务调度。
此时中断剩余部分只有两个内容:POPALL和RETI,再没有其他内容,更不用说重要内容了。如果不需调度,中断正常完成,否则,保存现场到TCB,切换任务到高优先级。现场内容包括了断点返回地址,中断剩余部分没有被跳过,它保存在此任务的TCB里,下次调度到此任务时,OSCtxSw借用这个中断的剩余部分,直接返回到任务断点继续执行任务。因为OSCtxSw也是模拟一次中断,栈与这个中断的剩余部分一模一样,可以直接借用。
中断的重要工作均被完成后才会切换任务,最后一个中断剩余的退出部分让给OSCtxSw,假想成发生了一次切换中断(切换中断模拟的就是硬件中断),而不是硬件中断。
第一次任务切换时没有中断栈,就人工模拟一个。在硬件中断里本身就有一个栈,那就直接拿过来用。
***************************************************************************************************
看了您的大作,对其中无任务切换中断的处理好象有点想不通。你在串口中断程序中不作任务切换,所以不调用OSIntEnter(),OSIntNesting没增加;当有更高级别的中断嵌套发生时,则有可能发生任务切换。这样串口中断的堆栈压入和弹出分别在不同任务堆栈中,这样是否会造成堆栈混乱?
是不是即使无任务切换中断处理都应成对调用OSIntEnter()和 OSIntExit()?如果调用会有什么坏影响吗?
SerialISR:
USING 0
PUSHALL
CLR EA
LCALL _?serial
SETB EA
POPALL
RETI
在以上串口中断程序中,从SETB EA 之后到RETI之间,仍然可被高级别中断打端,有可能发生任务切换。即使任务的堆栈不乱,那串口中断程序何时能得以继续运行也是问题,相当于中端程序中调了一个花费很长时间的子程序, 这样理解对吗?
同意你的观点!
我主要是想节省一个任务号,同时提高效率,任务切换太费时间了,中断的标准处理也比较烦琐,所以当时采用了关闭中断的临界资源处理方式,现在看来这么做是有问题的,这是个BUG。你把它按标准中断的处理方式做就可以解决这个问题。本来想偷机取巧来着,没想到漏了破绽,呵呵。
文章评论(0条评论)
登录后参与讨论