FreeRTOS——支持時間片

所謂時間片就是同一個優先級任務下可以有多個任務,每個任務輪流的享有相同的CPU時間,享有CPU的時間我們叫做時間片。在RTOS中,最小的時間單位是一個tick,即 SysTick 的中斷週期。對於FreeRTOS,時間片只能是一個tick。與其說FreeRTOS支持時間片,倒不如說他的時間片就是正常的任務調度。

一、原理分析

之 所 以 在 同 一 個 優 先 級 下 可 以 有 多 個 任 務 , 最 終 還 是 得 益 於taskRESET_READY_PRIORITY()和taskSELECT_HIGHEST_PRIORITY_TASK()這兩個函函數的實現方法。接下來我們分析下這兩個函數是如何在同一個優先級下有多個任務的時候起作用的。
系統在任務切換的時候總會從就緒列表中尋找優先級最高的任務來執行,尋找優先級最高的任務這個功能由taskSELECT_HIGHEST_PRIORITY_TASK()函數來實現,該函數在task.c 中定義。
taskSELECT_HIGHEST_PRIORITY_TASK()函數

#define taskSELECT_HIGHEST_PRIORITY_TASK()\
 {\
	 UBaseType_t uxTopPriority;\
	 /* 尋找就緒任務的最高優先級 */\ (1)
	 portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );\
	 /* 獲取優先級最高的就緒任務的 TCB,然後更新到 pxCurrentTCB */\ (2)
	 listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB,\
	 &( pxReadyTasksLists[ uxTopPriority ] ) );\
 }

(1) : 尋 找 就 緒 任 務 的 最 高 優 先 級 。 即 根 據 優 先 級 位 圖 表
uxTopReadyPriority 找到就緒任務的最高優先級,然後將優先級暫存在 uxTopPriority。
(2): 獲取優先級最高的就緒任務的 TCB,然後更新到 pxCurrentTCB。
目前我們的實驗是在優先級 2 上有任務 1 和任務 2,假設任務 1 運行了一個 tick,那接下來再從對應優先級 2 的就緒列表上選擇任務來運行就應該是選擇任務 2?怎麼選擇,代碼上怎麼實現?奧妙就在 listGET_OWNER_OF_NEXT_ENTRY()函數中,該函數在 list.h 中定義。
listGET_OWNER_OF_NEXT_ENTRY()函數

 #define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )\
 {\
	 List_t * const pxConstList = ( pxList );\
	 /* 節點索引指向鏈表第一個節點調整節點索引指針,指向下一個節點,
	 如果當前鏈表有 N 個節點,當第 N 次調用該函數時, pxIndex 則指向第 N 個節點 */\
	 ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;\
	 /* 當遍歷完鏈表後, pxIndex 回指到根節點 */\
	 if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )\
	 {\
	 	( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;\
	 }\
	 /* 獲取節點的 OWNER,即 TCB */\
	 ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;\
 }

listGET_OWNER_OF_NEXT_ENTRY()函數的妙處在於它並不是獲取鏈表下的第一個節點的 OWNER,而是用於獲取下一個節點的 OWNER。有下一個那麼就會有上一個的說法,怎麼理解?假設當前鏈表有 N 個節點,當第 N 次調用該函數時, pxIndex 則指向第 N個節點, 即每調用一次, 節點遍歷指針 pxIndex 則會向後移動一次,用於指向下一個節點。
本實驗中,優先級 2 下有兩個任務,當系統第一次切換到優先級爲 2 的任務(包含了任務 1 和任務 2,因爲它們的優先級相同) 時, pxIndex 指向任務 1, 任務 1 得到執行。 當任務 1 執行完畢,系統重新切換到優先級爲 2 的任務時, 這個時候 pxIndex 指向任務 2,任務 2 得到執行, 任務 1 和任務 2 輪流執行,享有相同的 CPU 時間, 即所謂的時間片。
本實驗中,任務 1 和任務 2 的主體都是無限循環,那如果任務 1 和任務 2 都會調用將自己掛起的函數(實際運用中,任務體都不能是無限循環的,必須調用能將自己掛起的函數) ,比如 vTaskDelay()。 調用能將任務掛起的函數中,都會先將任務從就緒列表刪除,然 後 將 任 務 在 優 先 級 位 圖 表 uxTopReadyPriority 中 對 應 的 位 清 零 , 這 一 功 能 由taskRESET_READY_PRIORITY()函數來實現, 該函數在 task.c 中定義。
taskRESET_READY_PRIORITY()函數

 #define taskRESET_READY_PRIORITY( uxPriority )\
 {\
	 if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) )\
	 == ( UBaseType_t ) 0 )\
	 {\
		 portRESET_READY_PRIORITY( ( uxPriority ),\
		 ( uxTopReadyPriority ) );\
	 }\
 }

taskRESET_READY_PRIORITY() 函 數 的 妙 處 在 於 清 除 優 先 級 位 圖 表uxTopReadyPriority 中相應的位時候,會先判斷當前優先級鏈表下是否還有其它任務,如果有則不清零。 假設當前實驗中,任務 1 會調用 vTaskDelay(),會將自己掛起,只能是將任務 1 從就緒列表刪除,不能將任務 1 在優先級位圖表 uxTopReadyPriority 中對應的位清 0,因爲該優先級下還有任務 2,否則任務 2 將得不到執行。

參考:[野火®]《FreeRTOS 內核實現與應用開發實戰—基於STM32》

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