μC/OS-Ⅱ總是運作進入就緒态任務中優先級最高的那一個。确定哪個任務優先級最高,
下面該哪個任務運作了的工作是由排程器(Scheduler)完成的。任務級的排程是由函數
OSSched()完成的。中斷級的排程是由另一個函數OSIntExt() 完成的,這個函數将在以後描
述。OSSched() 的代碼如下:
void OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
INT8U y;
OS_ENTER_CRITICAL();
if ((OSIntNesting == 0) && (OSLockNesting == 0))
{ /* 隻有ISR完成同時沒有鎖住排程才進行切換 */
//找最高優先級的任務
y = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
if (OSPrioHighRdy != OSPrioCur)
{ /*檢查尋找到的優先級最高的任務是否是目前正在運作的任務,若是則不進行排程*/
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];//指向優先級最高的任務控制塊
OSCtxSwCtr++; /* 切換計數器加*/
OS_TASK_SW(); /* 進行實際的任務切換*/
}
OS_EXIT_CRITICAL();
#define uCOS 0x80 /*用于任務切換的中斷向量*/
#define OS_TASK_SW() asm INT uCOS
μC/OS-Ⅱ任務排程所花的時間是常數,與應用程式中建立的任務數無關。任務切換很簡單,由以下兩步完成,将被挂起任務的微處理器寄存器推入堆棧,然後将較高優先級的任務的寄存器值從棧中恢複到寄存器中。在μC/OS-Ⅱ中,就緒任務的棧結構總是看起來跟剛剛發生過中斷一樣,所有微處理器的寄存器都儲存在棧中。換句話說,μC/OS-Ⅱ運作就緒态的任務所要做的一切,隻是恢複所有的CPU 寄存器并運作中斷傳回指令。為了做任務切換,運作OS_TASK_SW(), 人為模仿了一次中斷。多數微處理器有軟中斷指令或者陷阱指令TRAP 來實作上述操作。中斷服務子程式或陷阱處理(Trap hardler),也稱作事故處理(exception handler),必須提供中斷向量給彙編語言函數OSCtxSw() 。OSCtxSw() 除了需要OS_TCBHighRdy 指向即将被挂起的任務,還需要讓目前任務控制塊OSTCBCur 指向即将被挂起的任務,
OSSched()的所有代碼都屬臨界段代碼。在尋找進入就緒态的優先級最高的任務過程中,
為防止中斷服務子程式把一個或幾個任務的就緒位置位,中斷是被關掉的。為縮短切換時間,
OSSched()全部代碼都可以用彙編語言寫。為增加可讀性,可移植性和将彙編語言代碼最少
化,OSSched() 是用C 寫的。
OSCtxSw代碼:
_OSCtxSw PROC FAR
PUSHA ; 儲存目前任務的上下文
PUSH ES ;
PUSH DS ;
MOV AX, SEG _OSTCBCur ; Reload DS in case it was altered
MOV DS, AX ;
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 ;
LES BX, DWORD PTR DS:_OSTCBHighRdy ; SS:SP = OSTCBHighRdy->OSTCBStkPtr
MOV SS, ES:[BX+2] ;
MOV SP, ES:[BX] ;
POP DS ; 加載新任務的上下文
POP ES ;
POPA ;
IRET ; 傳回到新任務中
_OSCtxSw ENDP
3. 給排程器上鎖和開鎖(Locking and UnLocking the Scheduler)
給排程器上鎖函數OSSchedlock()用于禁止任務排程,直到任務完成後調用給排程器開鎖函數OSSchedUnlock() 為止。調用OSSchedlock() 的任務保持對CPU 的控制權,盡管有個優先級更高的任務進入了就緒态。然而,此時中斷是可以被識别的,中斷服務也能得到(假設中斷是開着的)。OSSchedlock() 和OSSchedUnlock() 必須成對使用。變量OSLockNesting 跟蹤OSSchedLock() 函數被調用的次數,以允許嵌套的函數包含臨界段代碼,這段代碼其它任務不得幹預。μC/OS-Ⅱ允許嵌套深度達255 層。當OSLockNesting 等于零時,排程重新得到允許。函數OSSchedLock() 和OSSchedUnlock() 的使用要非常謹慎,因為它們影響μC/OS-Ⅱ對任務的正常管理。
當OSLockNesting 減到零的時候, OSSchedUnlock() 調用OSSched[L3.10(2)] 。
OSSchedUnlock() 是被某任務調用的,在排程器上鎖的期間,可能有什麼事件發生了并使一
個更高優先級的任務進入就緒态。
調用OSSchedLock() 以後,使用者的應用程式不得使用任何能将現行任務挂起的系統調
用。也就是說,使用者程式不得調用OSMboxPend() 、OSQPend() 、OSSemPend() 、
OSTaskSuspend(OS_PR1O_SELF) 、OSTimeDly() 或OSTimeDlyHMSM(), 直到OSLockNesting 回零為止。因為排程器上了鎖,使用者就鎖住了系統,任何其它任務都不能運作。
當低優先級的任務要發消息給多任務的郵箱、消息隊列、信号量時,使用者不希望高優先級的任務在郵箱、隊列和信号量沒有得到消息之前就取得了CPU 的控制權,此時,使用者可以使用禁止排程器函數。
給排程器上鎖
void OSSchedLock (void)
if (OSRunning == TRUE)
OSLockNesting++;
給排程器開鎖.
void OSSchedUnlock (void)
if (OSRunning == TRUE) {
if (OSLockNesting > 0) {
OSLockNesting--;
if ((OSLockNesting | OSIntNesting) == 0) {
OSSched();
} else {
作者:
洞庭散人出處:
http://phinecos.cnblogs.com/
本部落格遵從
Creative Commons Attribution 3.0 License,若用于非商業目的,您可以自由轉載,但請保留原作者資訊和文章連結URL。