OSIntCtxSw()

在µC/OS-II中,由於中斷的產生可能會引起任務切換,在中斷服務程序的最後會調用OSIntExit()函數檢查任務就緒狀態,如果需要進行任務切換,將調用OSIntCtxSw()。所以OSIntCtxSw()又稱爲中斷級的任務切換函數。由於在調用OSIntCtxSw()之前已經發生了中斷,OSIntCtxSw()將默認CPU寄存器已經保存在被中斷任務的堆棧中了。

程序清單L9.5給出的代碼大部分與OSCtxSw()的代碼相同,不同之處是,第一,由於中斷已經發生,此處不需要再保存CPU寄存器(沒有PUSHA, PUSH ES, 或PUSH DS);第二,OSIntCtxSw()需要調整堆棧指針,去掉堆棧中一些不需要的內容,以使堆棧中只包含任務的運行環境。圖F9.5可以幫助讀者理解這一過程。

程序清單L 9.5	OSIntCtxSw().
_OSIntCtxSw PROC   FAR
;                ; Ignore calls to OSIntExit and OSIntCtxSw
;   ADD  SP,8    ; (Uncomment if OS_CRITICAL_METHOD is 1, see OS_CPU.H)	(1)
    ADD  SP,10   ; (Uncomment if OS_CRITICAL_METHOD is 2, see OS_CPU.H)
;
    MOV  AX, SEG _OSTCBCur               ; 載入DS
    MOV  DS, AX                            
;
    LES  BX, DWORD PTR DS:_OSTCBCur      ; OSTCBCur->OSTCBStkPtr = SS:SP	(2)
    MOV  ES:[BX+2], SS                    
    MOV  ES:[BX+0], SP                   
;
    CALL FAR PTR _OSTaskSwHook	(3)
;
    MOV  AX, WORD PTR DS:_OSTCBHighRdy+2 ; OSTCBCur = OSTCBHighRdy	(4)
    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	(5)
    MOV  BYTE PTR DS:_OSPrioCur, AL
;
    LES  BX, DWORD PTR DS:_OSTCBHighRdy  ; SS:SP = OSTCBHighRdy->OSTCBStkPtr	(6)
    MOV  SS, ES:[BX+2]             
    MOV  SP, ES:[BX]               
;
    POP  DS                              ; 載入新任務的CPU環境	(7)
    POP  ES                              	(8)
    POPA                                 	(9)
;
    IRET                                 ; 返回新任務	(10)
;
_OSIntCtxSw ENDP

當中斷髮生後,CPU在完成當前指令後,進入中斷處理過程。首先是保存現場,將返回地址壓入當前任務堆棧,然後保存狀態寄存器的內容。接下來CPU從中斷向量處找到中斷服務程序的入口地址,運行中斷服務程序。在µC/OS-II中,要求用戶的中斷服務程序在開頭保存CPU其他寄存器的內容[圖F9.5(1)]。此後,用戶必須調用OSIntEnter()或着把全局變量OSIntNesting加1。此時,被中斷任務的堆棧中保存了任務的全部運行環境。在中斷服務程序中,有可能引起任務就緒狀態的改變而需要任務切換,例如調用了OSMboxPost(), OSQPostFront(),OSQPost(),或試圖喚醒一個優先級更高的任務(調用OSTaskResume()),還可能調用 OSTimeTick(), OSTimeDlyResume()等等。
µC/OS-II要求用戶在中斷服務程序的末尾調用OSIntExit(),以檢查任務就緒狀態。在調用OSIntExit()後,返回地址會壓入堆棧中[圖F9.5(2)]。
進入OSIntExit()後,由於要訪問臨界代碼區,首先關閉中斷。由於OS_ENTER_CRITICAL()可能有不同的操作(見9.03.02節),狀態寄存器SW的內容有可能被壓入堆棧[圖F9.5(3)]。如果確實要進行任務切換,指針OSTCBHighRdy將指向新的就緒任務的OS_TCB,OSIntExit()會調用OSIntCtxSw()完成任務切換。注意,調用OSIntCtxSw()會在再一次在堆棧中保存返回地址[圖F9.5(4)]。在進行任務切換的時候,我們希望堆棧中只保留一次中斷髮生的任務環境(如圖F9.5(1)),而忽略掉由於函數嵌套調用而壓入的一系列返回地址(圖F9.5(2),(3),(4))。忽略的方法也很簡單,只要把堆棧指針加一個固定的值就可以了[圖F9.5(5)/程序清單L9.5(1)]。如果用方法2實現OS_ENTER_CRITICAL(),這個固定值是10;如果用方法1,則是8。實際操作中還與編譯器以及編譯模式有關。例如,有些編譯器會爲OSIntExit()在堆棧中分配臨時變量,這都會影響具體佔用堆棧的大小,這一點需要提醒用戶注意。
一但堆棧指針重新定位後,就被保存到將要被掛起的任務OS_TCB中[圖F9.5(6)/程序清單L9.5(2)]。在µC/OS-II中(包括µC/OS),OSIntCtxSw()是唯一一個與編譯器相關的函數,也是用戶問的最多的。如果您的系統移植後運行一段時間後就會死機,就應該懷疑是OSIntCtxSw()中堆棧指針重新定位的問題。
噹噹前任務的現場保存完畢後,用戶定義的對外接口函數OSTaskSwHook()會被調用[程序清單L9.5(3)]。注意到OSTCBCur指向當前任務的OS_TCB,OSTCBHighRdy指向新任務的OS_TCB。在函數OSTaskSwHook()中用戶可以訪問這兩個任務的OS_TCB。如果不用對外接口函數,請在頭文件中關閉相應的開關選項,提高任務切換的速度。
從對外接口函數OSTaskSwHook()返回後,由於任務的更替,變量OSTCBHighRdy被拷貝到OSTCBCur中[程序清單L9.5(4)],同樣,OSPrioHighRdy被拷貝到OSPrioCur中[程序清單L9.5(5)]。此時,OSIntCtxSw()將載入新任務的CPU環境,首先從新任務OS_TCB中取出SS和SP寄存器的值[圖F9.5(7)/程序清單L9.5(6)],然後運行POP DS [圖F9.5(8)/程序清單L9.5(7)], POP ES [圖F9.5(9)/程序清單L9.5(8)], POPA[圖F9.5(10)/程序清單L9.5(9)]取出其他寄存器的值,最後用中斷返回指令IRET [圖F9.5(11)/程序清單L9.5(10)]完成任務切換。
需要注意的是在運行OSIntCtxSw()和用戶定義的OSTaskSwHook()函數期間,中斷是禁止的。

圖F 9.5 中斷級任務切換時的80x86堆棧結構


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章