uc/os-ii任务调度(二)

任务调度关键是任务运行环境的切换。任务运行环境包括以下:
1. 程序的断点地址(PC
2. 程序状态字寄存器(xPSR
3. 通用寄存器内容
4. 任务堆栈指针(SP
其中1、2、3保存在任务堆栈中,4保存在任务的任务控制块中。
程序切换的关键是把程序的私有堆栈指针赋予处理器的堆栈指针PSP。

  • 这里主要分析中断级的调度OSIntExt()

当一个中断处理函数退出时,OSIntExit()会被调用来决定是否有优先级更高的任务需要执行。如果有则调用OSIntCtxSw()做任务切换。

void  OSIntExit (void)
{
#if OS_CRITICAL_METHOD == 3                                /* Allocate storage for CPU status register */
    OS_CPU_SR  cpu_sr = 0;
#endif

    if (OSRunning == OS_TRUE) {
        OS_ENTER_CRITICAL();
        if (OSIntNesting > 0) {                            /* Prevent OSIntNesting from wrapping       */
            OSIntNesting--;
        }
        if (OSIntNesting == 0) {                           /* Reschedule only if all ISRs complete ... */
            if (OSLockNesting == 0) {                      /* ... and not locked.                      */
                OS_SchedNew();
                if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy */
                    OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];
#if OS_TASK_PROFILE_EN > 0
                    OSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task  */
#endif
                    OSCtxSwCtr++;                          /* Keep track of the number of ctx switches */
                    OSIntCtxSw();                          /* Perform interrupt level ctx switch       */
                }
            }
        }
        OS_EXIT_CRITICAL();
    }
}

汇编函数OSIntCtxSw()源码如下,其作用是触发PendSV中断

OSIntCtxSw
    LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR

看到这里可能奇怪怎么OSCtxSw()OSIntCtxSw()完全一样,事实上,这两个函数的意义是不一样的,OSCtxSw()做的是任务之间的切换,如任务A因为等待某个资源或是做延时切换到任务B,而OSIntCtxSw()则是中断退出时,由中断状态切换到另一个任务。由中断切换到任务时,CPU寄存器入栈的工作已经做完了,所以无需做第二次了(参考邵老师书的3.10节)。这里只不过由于CM3的特殊机制导致了在这两个函数中只要做触发PendSV中断即可,具体切换由PendSV中断来处理。
前面已经说过真正的任务切换是在PendSV中断处理函数里做的,由于CM3在中断时会有一半的寄存器自动保存到任务堆栈里,所以在PendSV中断处理函数中只需保存R4-R11并调节堆栈指针即可。

=========中断级的任务调度就分析这么多==========

  • 之前关于任务调度有过以下疑惑:在任务级调度函数中OS_TASK_SW()函数是在临界段中执行的,即已经通过将PRIMASK写入1关闭所有可屏蔽的中断。那么在OS_TASK_SW()中触发的PendSV异常还能够得到响应吗?虽然知道结果是一定可以响应的,否则任务怎么切换呢。可是不明白为什么中断关闭了还能响应PendSV异常呢?
void  OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3                            /* Allocate storage for CPU status register     */
    OS_CPU_SR  cpu_sr = 0;
#endif
    OS_ENTER_CRITICAL();
    if (OSIntNesting == 0) {                           /* Schedule only if all ISRs done and ...       */
        if (OSLockNesting == 0) {                      /* ... scheduler is not locked                  */
            OS_SchedNew();
            if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy     */
                OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
#if OS_TASK_PROFILE_EN > 0
                OSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task      */
#endif
                OSCtxSwCtr++;                          /* Increment context switch counter             */
                OS_TASK_SW();                          /* Perform a context switch                     */
            }
        }
    }
    OS_EXIT_CRITICAL();
}

其实OS_Sched()执行顺序应该是这样的:
1. 关中断OS_ENTER_CRITICAL();
2. 获取最高优先级的就绪任务OS_SchedNew();
3. 触发PendSV异常OSCtxSw();注意这里触发的异常只是被挂起,并没有立即得到响应。
4. 开中断并推出函数OS_EXIT_CRITICAL();
5. 开中断后立即进入OS_CPU_PendSVHandler()PendSV异常服务程序进行任务切换。
中断被屏蔽后被挂起的异常会一直挂起,知道打开中断才会进入异常服务程序。

同理,在中断级的任务调度中执行OSIntCtxSw()后挂起PendSV异常,要等退出中断服务函数后在进入OS_CPU_PendSVHandler()PendSV异常服务程序进行任务切换。

参考:http://www.stmcu.org/module/forum/thread-384142-1-1.html

你可能感兴趣的:(ucos-ii)