天天看點

uC/OS-II源碼分析(六)

μ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。