FREERTOS之任務調度vPortYield

URL: http://zyq5510806.blog.163.com/blog/static/122738986201282665122797/

2009-05-13 聲明:本篇文章主要參考了http://blog.ednchina.com/bluehacker
freertos支持多個任務具有相同的優先級,因此,當它被配置爲可搶佔內核時,調度算法既支持基於優先級的調度,也支持時間片輪流調度。任何時候調度器運行時它都選擇處於就緒狀態下的優先級最高的那個任務;如果有多個任務處於同一優先級,則freertos每個時鐘節拍的中斷服務程序中,將對這些任務應用換調度算法,輪流執行這些任務。
系統用uxTopReadyPriority全局變量記錄當前處於就緒態的任務的最高優先級。調度的時候就根據這個uxTopReadyPriority直接找到就緒鏈表中pxReadyTasksLists[ uxTopReadyPriority ]的任務,進行運行。
一個任務可以通過調用taskYIELD()讓出cpu,從而調度令一個任務運行。它的實現如下:
#define taskYIELD() portYIELD()
portYIELD()是一個體繫結構相關的函數,對於不同的mcu需要實現這麼一個函數完成調度。我拿atmelatmega323 mcu爲例子,說明下具體實現。
extern void vPortYield( void ) __attribute__ ( ( naked ) );
#define portYIELD() vPortYield()
* Manual context switch. The first thing we do is save the registers so we
* can use a naked attribute.
void vPortYield( void ) __attribute__ ( ( naked ) );
void vPortYield( void )
{
portSAVE_CONTEXT();
vTaskSwitchContext();
portRESTORE_CONTEXT();
asm volatile ( "ret" );
}
portYIELD()就是vportYield(),它保存現場,然後調用vTaskSwitchContext()這個函數選擇下一個運行的任務,然後portRESTORE_CONTEXT()完成任務切換。
void vTaskSwitchContext( void )
{
traceTASK_SWITCHED_OUT();
if( uxSchedulerSuspended != ( unsigned portBASE_TYPE ) pdFALSE )
{
/* 當前調度器被禁止,因此不允許調度,設xMissedYield=TRUE*/
xMissedYield = pdTRUE;
return;
}
taskCHECK_FOR_STACK_OVERFLOW();
/* 找到包含有就緒任務的最高優先級隊列 */
while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopReadyPriority ] ) ) )
{
--uxTopReadyPriority;
}
/* listGET_OWNER_OF_NEXT_ENTRY 從最高優先級隊列上取下一個任務,設爲pxCurrentTCB,即馬上將要切換到該任務運行*/
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB,&( pxReadyTasksLists[ uxTopReadyPriority ] ) );
traceTASK_SWITCHED_IN();
vWriteTraceToBuffer();
}
這裏注意的是listGET_OWNER_OF_NEXT_ENTRY()宏並不是簡單的從隊列中取下第一個任務,而是walk through這個隊列,比如上一次調度它從這個隊列上取下的是第一個任務,那麼這次調度選中的則是該隊列中的第2個任務。這樣就保證了同一優先級的多個任務之間公平的平分處理器時間。選中任務後(用pxCurrentTCB指向它)。那麼在portRESTORE_CONTEXT()中就完成最後的切換。因此這個地方有些有趣,函數vTaskSwitchContext()從名稱看給人感覺是完成任務切換的,但是其實並不是這樣,它只完成選擇下一個運行的任務(也就是將要切換過去的任務),真正的切換時在portRESTORE_CONTEXT()中就完成的。
任務調度還可以發生在時鐘節拍中斷isr中,這個當然也是與cpu體系結構相關的。仍然以atmega323爲例。它用的是定時器1的比較中斷A作爲時鐘節拍產生器。其中斷isr是:
void SIG_OUTPUT_COMPARE1A( void ) __attribute__ ( ( signal, naked ) );
void SIG_OUTPUT_COMPARE1A( void )
{
vPortYieldFromTick();
asm volatile ( "reti" );
}
vPortYieldFromTick()就是完成調度。代碼如下:
void vPortYieldFromTick( void ) __attribute__ ( ( naked ) );
void vPortYieldFromTick( void )
{
//保存現場
portSAVE_CONTEXT();
/*檢查延時任務鏈表,如果發現有任務延時已經到期,則將該任務加到就緒鏈表*/
vTaskIncrementTick();
//挑選下一個運行的任務,準備切換過去
vTaskSwitchContext();
//完成任務切換
portRESTORE_CONTEXT();
asm volatile ( "ret" );
}

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