深度解剖~ FreeRtos閱讀筆記2 任務建立、內核鏈表初始化

2.FREERTOS任務建立、內核鏈表初始化

硬件環境:cortex m4數組

FreeRTOS版本:v8.0.1函數

今天開始閱讀freertos,閱讀同時作下筆記,等哪天碰到移植問題再翻出來看看。spa

 

2.1 任務、鏈表結構體

源碼中使用tskTCB來存儲一個任務的全部信息,xLIST存儲內核鏈表數據。一個系統最基本的功能是它的任務調度,在任務切換時最重要的則是內核鏈表,用圖描述下這兩個結構體,這樣看起來比代碼更清晰。(TCB中有省略成員)翻譯

(TCB結構體)指針

-------------------------------------------------分割線----------------------------------------------------索引

(LIST結構體)圖片

一個TCB中包含了兩個xLIST_ITEM做爲鏈表節點,操做xLIST_ITEM中的指針指向既爲控制一個任務進出某個鏈表。相比xLIST_ITEM,在xLIST中使用了精簡版的結點xMINI_LIST_ITEM。內存

 

2.2 xTaskGeneEricCreate 任務建立流程分析

xTaskGeneEricCreate 函數用來建立一個新任務,在調度器啓動前和啓動後均可以建立。資源

Freertos在調度器啓動後至少會有一個任務(IDLE)處於準備調度狀態,即便開發者不去建立本身的任務。開發

xTaskGeneEricCreate源碼流程:(拖動能夠放大圖片)

(任務建立流程圖)

 

2.2.1 prvAllocateTCBAndStack 分配空間

Freertos使用pvPortMalloc在堆上分配一塊TCB大小的內存空間,分配成功後還要使用pvPortMalloc分配一塊內存,當作任務運行所須要的棧空間。這些空間直到任務被刪除時纔會獲得釋放。

在棧分配時有參數判斷,若建立任務時有傳入的棧地址則放棄分配。

分配的棧內存總大小爲棧深度(傳入參數)與棧寬度乘積。

 

2.2.2 prvInitialiseTCBVariables 執行初始化

prvInitialiseTCBVariables函數中主要執行了任務名字拷貝、優先級保存、兩個鏈表節點初始化。

下圖表示TCB中節點和鏈表初始化後指針指向

(鏈表及TCB初始化後)

 

2.2.3 pxPortInitialiseStack 執行」壓棧」

pxPortInitialiseStack函數執行的代碼很是奇特,一開始徹底沒法理解,還好在葵花寶典找到了da案,神書!引用M3權威指南上一句翻譯:響應異常的第一個步驟是保存現場,硬件自動壓棧,壓棧後內存分佈:

 

 

再對比看下pxPortInitialiseStack源碼:

對比下二者動做,這個函數是對任務棧進行了一些處理,而且是模仿異(中斷)常發生時所產生的動做。爲何必定要模仿異常進行壓棧,首先扯一下freertos任務調度工做的大體流程:

當一個任務在運行時,還有一個內部定時器(systick)在一直計數,它的計數值和時鐘頻率比值能夠當作爲時間片。時間片到,中斷產生,中斷裏進行上下文切換也就是pxReadyTasksLists中的任務被依次調度。硬件進入中斷時便會自動壓棧,不須要咱們處理。中斷處理完成後到中斷返回時硬件還會自動出棧,還原進入異常前的狀態。進中斷時壓入的那些寄存器值都被一一出棧 如:PC、R0、等寄存器。這樣pxPortInitialiseStack函數就好理解了,它先對新建立的任務進行手動壓棧,還多包括了R4-R11,那麼在調度中斷結束後這些手動壓入的值將被自動出棧,進而使新任務運行起來。

PC位置是傳入的任務主程序句柄地址,也就是咱們要任務執行的主要程序,LR(返回寄存器)的位置是prvTaskExitError函數地址,這個函數裏是一個for死循環加錯誤信息打印,也就是一個任務永遠不該從它的主程序中跳出,若是跳出則進入prvTaskExitError函數打印錯誤。通常任務句柄裏都會用for(;;)把它寫死永遠循環執行,須要退出時要將該任務delete掉。

 

2.3 pxReadyTasksLists鏈表

一個TCB建立並初始化完成後便開始插入pxReadyTasksLists等待被調度。pxReadyTasksLists鏈表是一個數組,優先級最大數決定它的大小。一個處於空閒狀態的TCB(準備好被調度)在插入時是an照優先級做爲索引插入的,這裏說TCB插入不太準確,應該是TCB上的鏈表節點插入鏈表。

舉個栗子,第一個任務插入空鏈表時的情況:

 

看着有些凌亂的話再來張大意圖:

 

若是此時又有一個相同優先級任務建立,鏈表變爲:


 

簡略圖:

 

 

鏈表將節點依次鏈接,組成TCB鏈,調度器運行時會an照須要遍歷鏈表進而控制任務。

鏈表頭部都帶有index元素,一開始它指向鏈表自己,因此咱們上面建立的任務都像是在尾插,事實上調度器運行起來時新節點插入的位置由index決定。

圖解:

調度開始後index開始遍歷readylist,它指向第一個TCB時,第一個TCB獲得cpu資源開始運行,變爲:

 

注意紅色線條變化,此時若是動態建立了一個優先級相同的任務TCB3,應該把它插在哪裏?若是插在TCB後面那對於TCB2來講是不公平的,由於人家排隊等待cpu的時間確定比TCB3長,其實仔細考慮下插在鏈表頭部或尾部都是不規律的,只有利用index。Freertos將其插在TCB前面,以保證是當前鏈表最後一個獲得cpu資源的位置:

 

 

新TCB進入鏈表,任務建立流程就快結束了。在程序尾部有些優先級判斷,若是建立的任務比當前運行的任務優先級要高則使能PendSV中斷。若是調度器是中止的則直接更改當前TCB指針。

相關文章
相關標籤/搜索