原创 关于中断处理的系列问题

2011-5-3 18:06 1795 11 11 分类: MCU/ 嵌入式
关于中断处理的系列问题
中断的任务被调度后,再度获得运行时是从任务被中断处执行。

    中断的任务被调度后,再度获得运行时是从任务被中断处执行。
    在中断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条评论)

登录后参与讨论
我要评论
0
11
关闭 站长推荐上一条 /2 下一条