Ucos中任务调度过程中任务切换分析

本文解析了UCOS-II操作系统中任务调度过程的任务切换机制。重点介绍了如何通过任务调度器函数OSSched()确定最高优先级任务,并通过OS_TASK_SW()宏完成实际的任务切换。此外,深入分析了OSCtxSw函数实现的任务上下文保存与加载过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Ucos中任务调度过程中任务切换分析


Ucos中任务调度过程中任务切换分析


μC/OS-Ⅱ总是运行进入就绪态任务中优先级最高的那一个。确定哪个任务优先级最高,下面该哪个任务运行了的工作是由调度器(Scheduler)完成的。任务级的调度是由函数OSSched()完成的。中断级的调度是由另一个函数OSIntExt()完成的,这个函数将在以后描述。OSSched()的代码如程序清单L3.8所示。
 程序清单 L3.8 任务调度器(the Task Scheduler)
void OS_Sched (void)
{
    INT8U y;
 
 
    OS_ENTER_CRITICAL();
if ((OSLockNesting | OSIntNesting) == 0) //写得太浪了
{                                                                  (1)
        y             = OSUnMapTbl[OSRdyGrp];                          (2)
        OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);   (2)
        if (OSPrioHighRdy != OSPrioCur) {                              (3)
            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];                (4)
            OSCtxSwCtr++;                                              (5)
            OS_TASK_SW();                                              (6)
        }
    }
    OS_EXIT_CRITICAL();
}
在上面的代码中如果满足一些条件能进行任务切换时则会利用宏调用OS_TASK_SW()来完成实际上的任务切换[L3.8(6)]。在ucos_II.h中:
#define  OS_STK_GROWTH        1  
#define  uCOS                 0x80                 
#define  OS_TASK_SW()         asm  INT   uCOS                 
实事上也就是人为的进行了一次中断,就是把8086CPU中的中断0x80号中断向量重写,而在主函数中又一段代码:
PC_VectSet(uCOS, OSCtxSw);
 
当然此函数的实现为:
void PC_VectSet (INT8U vect, void (*isr)(void))//一个是向量号,另一个是向量对应函数入口地址
{
#if OS_CRITICAL_METHOD == 3
    OS_CPU_SR  cpu_sr;
#endif    
    INT16U    *pvect;
    pvect    = (INT16U *)MK_FP(0x0000, vect * 4);  
    OS_ENTER_CRITICAL();
    *pvect++ = (INT16U)FP_OFF(isr);                 
    *pvect   = (INT16U)FP_SEG(isr);                  
    OS_EXIT_CRITICAL();
}
则PC_VectSet(uCOS, OSCtxSw)就是把0x80的中断向量的中断向量服务函数入口地址设置为OSCtxSw函数的地址.换句话说一旦进行OS_TASK_SW()则就会进行此中断向量所指向的中断服务函数中进行处理.
在看OSCtxSw前需要说明一点,当程序执行到OS_TASK_SW()时, OSTCBHighRdy已经确定下来了,准备好任务切换了.
下面就看看OSCtxSw的实现,它是用汇编写的:
(1)_OSCtxSw    PROC   FAR
(2)         PUSHA                    ; Save current task's context
(3)         PUSH   ES                              ;
(4)         PUSH   DS                              ;
 
            MOV    AX, SEG _OSTCBCur    ; Reload DS in case it was altered
            MOV    DS, AX                          ;
 
(5)         LES    BX, DWORD PTR DS:_OSTCBCur; OSTCBCur->OSTCBStkPtr = SS:SP
            MOV    ES:[BX+2], SS                   ;
            MOV    ES:[BX+0], SP                   ;
 
            CALL   FAR PTR _OSTaskSwHook  ; Call user defined task switch hook
 
            MOV    AX, WORD PTR DS:_OSTCBHighRdy+2 ; OSTCBCur = OSTCBHighRdy
            MOV    DX, WORD PTR DS:_OSTCBHighRdy   ;
            MOV    WORD PTR DS:_OSTCBCur+2, AX     ;
            MOV    WORD PTR DS:_OSTCBCur, DX       ;
 
            MOV    AL, BYTE PTR DS:_OSPrioHighRdy  ; OSPrioCur = OSPrioHighRdy
            MOV    BYTE PTR DS:_OSPrioCur, AL      ;
 
 (6)        LES    BX, DWORD PTR DS:_OSTCBHighRdy  ; 
;SS:SP = OSTCBHighRdy->OSTCBStkPtr
            MOV    SS, ES:[BX+2]                   ;
            MOV    SP, ES:[BX]                     ;
 
(7)         POP    DS                              ; Load new task's context
(8)         POP    ES                              ;
(9)         POPA                                   ;
 
(10)      IRET                ; Return to new task
 
_OSCtxSw    ENDP
 
            PAGE  
在80x86处理器上,任务调用OS_TASK_SW()执行软中断指令后,先向堆栈中压入返回地址(段地址和偏移量),然后是状态字寄存器SW(注这些操作都是由CPU自动完成的并且与当前任务本身无关)。紧接着用PUSHA, PUSH ES,和 PUSH DS保存任务运行环境(注意:这些与当前任务有关的寄存器中的数据存储在此任务开始时所建立的栈中)。最后用OSCtxSw()在任务OS_TCB中(注意不是栈了)保存SS和SP寄存器中的内容。
任务环境保存完后,将调用用户定义的对外接口函数OSTaskSwHook(),它用来将
请注意,此时OSTCBCur指向当前任务OS_TCB,OSTCBHighRdy指向新任务的OS_TCB。在OSTaskSwHook()中,用户可以访问这两个任务的OS_TCB。在这里我们不用去关心它.刚提到过因为OSTCBHighRdy这个数据已经指向将要执行的任务的TCB了.
则接下来一方面把OSTCBHighRdy里的数据放到OSTCBCur里,同时它们的指针OSPrioCur也要改为指向OSPrioHighRdy.这样对于整个系统来说当前任务的TCB里的数据就是将要被执行的那个任务的TCB的数据信息了.
但仅仅是这个还不够,因为当前的栈所保存的数据指的数据仍然是被替换的那个任务的数据,一旦把栈里内容弹出后果将不可预测,因为当前任务的TCB和它的数据信息不对应.那怎么办呢?对了就是改SS:SP,在弹出前将CPU的SS:SP指向目标任务的栈.因此我们可以从将要运行的任务的TCB中取出保存栈信息的数据,然后修改寄存器SS:SP,最后再利用出栈,就会使另一个任务开始执行.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值