歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

μC/OS中高優先級任務一旦處於就緒態總能獲得CPU控制權的理解

μCOS的任務切換時間:1.任務創建時 2.任務掛起時 3.任務恢復 4.任務延時時 6.任務釋放信號量時 7.任務釋放互斥信號量時 8.任務請求消息郵箱時 9.任務釋放消息隊列時 10 中斷退出時(OSINTEXIT()函數中) 

任務之前的切換應該就是利用時鐘中斷來實現,當OS運行完一個時鐘片後會產生一個中斷(定時器的中斷)異常,PC指針立即跳轉到異常向量表執行處理異常的代碼,隨後會導致OS執行一次任務調度。整個過程分析如下:

產生異常後執行的第一條指令

b     HandlerIRQ

跳轉到HandlerIRQ,而HandlerIRQ是一條宏命令,定義如下:

HandlerIRQ    HANDLER HandleIRQ

宏體如下:

MACRO

$HandlerLabel HANDLER $HandleLabel

$HandlerLabel

       sub  sp,sp,#4

       stmfd      sp!,{r0}                //後面代碼需要使用R0寄存器,所以先將其入棧。

ldr     r0,=$HandleLabel    //此處是將HandeIRQ的地址賦給寄存器R0,Handler中存放是中斷服務函數匯編部分的入口地址,此匯編用來計算中斷服務函數的入口地址,所有IRQ中斷通用。HandeIRQ的賦值操作在後面。

       ldr     r0,[r0]                    //此處讀取R0中的內容,即中斷服務函數匯編部分的入口地址

       str     r0,[sp,#4]       

       ldmfd   sp!,{r0,pc}            //出棧,棧中R0的值重新賦給R0,中斷服務函數匯編入口地址復制給PC。相當於跳轉到匯編入口地址處。

       MEND

HandeIRQ中存放匯編入口地址的代碼如下。

ldr   r0,=HandleIRQ               //取HandeIRQ地址

       ldr   r1, =OS_CPU_IRQ_ISR              //取OS_CPU_IRQ_ISR地址

       str   r1,[r0]                                 //將OS_CPU_IRQ_ISR地址賦值給HandeIRQ

 

       執行完宏體的結果應該是跳轉到OS_CPU_IRQ_ISR

OS_CPU_IRQ_ISR      

 

       STMFD   SP!, {R1-R3}                   //後面會皆用這三個寄存器,所以先將其入棧

       MOV     R1, SP                              //把IRQ模式下的當前SP指針保存到R1

       ADD     SP, SP, #12             //調整SP,使其指向R1~R3入棧前的地址

       SUB     R2, LR, #4              //LR保存的是返回值的地址,但是不同的異常產生的LR不同。IRQ異常時,LR保存的是下一條將被執行的指令+4。所以此處需要LR-4。來調整。

       MRS     R3, SPSR                          ; Copy SPSR (Task CPSR)  

 

       MSR     CPSR_cxsf, #SVCMODE|NOINT   //切換處理器運行狀態,SP也會更改為特權模式的堆棧指針

 

       下面代碼的作用是將被中斷的任務的相關寄存器保存到特權模式的堆棧下。

 

       STMFD   SP!, {R2}                        //R2中存放的是被中斷任務的下一條將被執行的指令,將其入棧。

       STMFD   SP!, {R4-R12, LR}           ; Push task''s LR,R12-R4     

       LDMFD   R1!, {R4-R6}                   //R1此時是IRQ堆棧的棧頂指針,和下一條指令一起將被中斷任務的R1~R3寄存器保存到特權模式的堆棧中

       STMFD   SP!, {R4-R6}                   ; Push Task''s R3-R1 to SVC stack

       STMFD   SP!, {R0}                     ; Push Task''s R0 to SVC stack    

       STMFD   SP!, {R3}                        ; Push task''s CPSR

       此時的特權模式堆棧結構應該是:

LR

R12

R11

R10

R9

R8

R7

R6

R5

R4

R3

R2

R1

R0

CPSR

       LDR     R0,=OSIntNesting        ;OSIntNesting++

       LDRB    R1,[R0]

       ADD     R1,R1,#1

       STRB    R1,[R0]                            //μC/OS中OSInrNesting變量+1

      

       CMP     R1,#1                   ;if(OSIntNesting==1){

       BNE     %F1

        

       LDR     R4,=OSTCBCur          // ;OSTCBHighRdy->OSTCBStkPtr=SP;

       LDR     R5,[R4]                             //將當前的TCB指針賦值R5     

       STR     SP,[R5]                 //將被特權模式的棧頂指針保存到北中斷任務的OSTCBStkPtr中

1

       MSR    CPSR_c,#IRQMODE|NOINT    ;Change to IRQ mode to use IRQ stack to handle interrupt

      

       LDR     R0, =INTOFFSET

    LDR     R0, [R0]

      

    LDR     R1, IRQIsrVect                          //IRQ已經通過指令

//IRQIsrVect DCD HandleEINT0被賦值為HandleEINT0的地址

    MOV     LR, PC                    //此時的PC應該指向MSR那條指令,LDR這條恰好是跳轉指令,這樣跳轉之後剛好返回MSR。

    LDR     PC, [R1, R0, LSL #2]         //此處計算C語言的中斷服務函數入口地址,然後跳轉執行。    

    MSR              CPSR_c,#SVCMODE|NOINT   //中斷服務函數執行完後返回

    BL          OSIntExit           //跳轉OSIntExit執行,先插入分析OSIntExit

void  OSIntExit (void)

{

#if OS_CRITICAL_METHOD == 3                             

    OS_CPU_SR  cpu_sr = 0;

#endif

    if (OSRunning == OS_TRUE) {

        OS_ENTER_CRITICAL();

        if (OSIntNesting > 0) {                     

            OSIntNesting--;

        }

        if (OSIntNesting == 0) {                          

            if (OSLockNesting == 0) {                    

                OS_SchedNew();                                      //計算處於最高優先級的任務

                if (OSPrioHighRdy != OSPrioCur) {        //假如就緒態的任務優先級高於當前運行的任務優先級著將其調度執行

                    OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];

#if OS_TASK_PROFILE_EN > 0

                    OSTCBHighRdy->OSTCBCtxSwCtr++;       

#endif

                    OSCtxSwCtr++;                        

                    OSIntCtxSw();                           //任務切換                        

                }

            }

        }

        OS_EXIT_CRITICAL();

    }

}

   

    LDMFD   SP!,{R4}              //將當前堆棧保存的數據彈出一個到R4,即將被中斷的任務的CPSR保存到R4

    MSR              SPSR_cxsf,R4

    LDMFD   SP!,{R0-R12,LR,PC}^     出棧,繼續執行被中斷的任務。

OSInt和OS_Sched類似但是不能互換使用。OSIntExit含有中斷嵌套數的計算。在OSIntExit做任務切換使用的是OSIntCtxSw,而OS_Sched中使用的是OS_TASK_SW。根據如上分析,每個時鐘片刻結束時候OS都會根據優先級重新調度一遍,所以說在μC/OS,只要高優先級的任務處於就緒態,就會立即搶占CPU。以為在這一個時鐘片結束後,OS會執行一次調度。

Copyright © Linux教程網 All Rights Reserved