uCOS-II中的任務切換-圖解多種任務調度時機與問題


【@.1 任務調度時機】

之前的一篇文章分析了具體的uCOS-II中的任務切換機制,是從函數調用的角度上分析的。這次我具體從整個程序運行的時間上來看,分析多種任務調度發生的時機。以下所有圖片均可點擊放大觀察。

所有圖中紅色箭頭表示中斷級的任務切換,藍色箭頭表示任務級的中斷切換。

image

1.僅有一個任務,這種情況最簡單。假設時鍾節拍是1000次每秒,由定時中斷產生,當節拍的時鍾服務程序結束時會調用OSInitExit,退出中斷,其中將進行上下文切換,運行當前就緒狀態優先級最高的任務,這里當然就是任務A、任務A中的代碼比較簡單,運行到最后時假設調用OSTimeDly(1)延時一個周期,提示系統放棄CPU控制權,這時將進行任務切換到空閑任務Idle Task。空閑任務優先級最低,是一個死循環,僅僅讓一個OSIdleCtr循環一次加一。可以看出,在一個時鍾節拍的間隔,這個計數器可能加不止一次。另外uCOS中還有一個統計任務,需配置打開,其中就是利用這個空閑計數器求出CPU有多少時間在空閑任務中,即有多少CPU占空比。通過圖示分析可以看出,很明顯雖然任務A延時一個時鍾周期,1ms,但是實際上將會少於1ms的延時。這就是為什么實際的延時中或多或少都會有延時抖動的現象,下面的很多例子的延時抖動都可能比這種情況更加復雜。

 

image

2.簡單的中斷,這里假設有一個外部中斷,在圖示處打斷了任務A的運行。外部中斷響應后進入服務函數,中斷退出時調用OSInitExit進行任務切換,回到剛才被中斷的任務。所以剛才的任務A就被延后了一段時間允許,之后任務A再切換到空閑任務。很明顯,這個時鍾周期內空閑計數器OSIdleCtr會少加一些,最后可以統計出CPU占用率會上升一些。

 

image

3.中斷函數中利用OSSemPost發送一個信號量,接受信號量的是任務B,其優先級大於任務A。於是在每次中斷服務函數結束時會首先調度任務B執行,按照圖中任務B的代碼只有一個OSSemPend可能會有等待掛起發送,之后任務B會因為等待接受信號量而調度會任務A繼續執行。圖中的畫出了三個外部中斷分別出現在不同時期,其中第三個外部中斷將導致任務B執行到一半遇到時鍾節拍的產生。這時候任務B會掛起,將執行節拍中斷服務程序,經過調度后會發現任務B任然處於就緒狀態,所以將繼續運行任務B直到結束。最后這里可以看出空閑任務在每個時鍾周期被擠得更少了,所以CPU的利用率更多。

 

image

4.跟第三中情況類似,只不過這里任務B中加了一句OSTimeDly(3)延時3個時鍾周期,這時可以知道,在任務B延時期間,只剩TaskA和空閑任務,他們將不會因為任務B的延時而被掛起。延時結束時,時鍾節拍將首先調度延時結束處於就緒狀態的任務B,之后再運行原本的任務A。這種情況也簡單的表示了uCOS是怎樣充分利用CPU資源的。

 

image

5.中斷中調用OSTaskResume恢復任務B。這里只是想說明,Resume/Suspend跟Post/Pend的區別,前者是一旦恢復則一直運行,而后者是Post一次,Pend方運行一次。

 

image

6.當用信號量通訊時,利用任務A來發送,控制高優先級的任務B接受信號量。可以看出,任務A發送一次信號量將會時任務B調度執行,此時任務A由於優先級低,被掛起,當任務B運行一次循環后又Pend等待信號量,這時任務B被掛起,由下一個時間周期的任務A發送,周而復始。

 

image

7.對於共享資源,可以采用圖示方式編寫。這種方式表明,當兩個任務想要使用共享資源時,每個任務用Pend和Post的組合包圍住想要操作的資源(比如一個函數,一個公共變量)。其中一個任務用完資源后執行Post,將會使另一個Pend等待資源的任務得以往下執行,不論二者的優先級如何。圖中的任務也包含了每次時鍾節拍對任務的延時作用。不過像這種信號量通訊的場合會涉及到優先級反轉問題,我們后面會分析。

 

image

8.這里演示了一個比較惡劣的中斷嵌套實例,可以看出所有任務/中斷級的任務調度只有等最后一層中斷嵌套執行結束后才能執行,在中斷嵌套中若使用Post或Resume想要執行任務調度也只能等到嵌套結束才能執行。uCOS-II支持中斷嵌套,不用擔心中斷的響應問題,但是一旦中斷嵌套之后就會出現很多延時的問題,將會導致整個系統的實時性下降,比如圖示的TaskA就因為中斷嵌套,並且因為中斷嵌套結束后TaskB接受到信號量而執行,使得其延時效果大大超出我們想象,而且中間還缺了一步時鍾周期,甚至導致這個周期滿負荷(空閑任務不會執行到)。雖然畫的有點誇張,但是這不得不引起我們的注意。所以通常推薦中斷服務函數寫的越短越好,一個好的方法是,清除了中斷標志后僅僅發送一個信號量通知別的任務執行,將所有需要花時間運行的工作交給任務而不是留給中斷服務函數處理。

 

以上的分析包含了少量幾個任務和間可能出現的常見情況,對於大型程序來說,任務的調度和中斷的響應可能比這個更加復雜,需要更進一步分析。

【@.2 優先級反轉】

image

圖示的情況將出現優先級反轉。任務A和C需要處理一個共享資源,低優先級的任務C首先得到信號量,處理完成后Post發送信號量通知等待中的任務A得以運行,任務A處理完共享信號量后Post,將使得等待中的任務C運行,而過了一段時間,任務B由於某種原因被恢復為就緒狀態,則任務B會搶奪任務C的CPU使用權,待任務B結束后任務C才得以進行,之后任務C發送信號量,通知任務A得以繼續運行。

這將會使得任務A等待信號量的時間延長,看上去是優先級低於A的任務B先於任務A執行,這往往也是我們所不希望的。這個問題的原因就是因為任務C的優先級太低,所以解決這個問題時簡單的方法就是動態修改進行共享資源操作的任務的優先級即可。於是引入了互斥信號量。

image

互斥信號量跟普通信號量基本一致,區別在於通過Pend得到信號量的任務將會被動態修改其優先級。這里我們一般會設置一個比較高的優先級,如圖所示,任務A和任務C交替得到信號量,並且得到信號量時被賦予了一個更高的優先級。這時若任務B進入就緒狀態就無法得以運行了。注意到這僅僅是uCOS-II的解決方法,因為uCOS-II不支持多任務同一優先級。

 

【@.3 uCOS中的中斷延時】

image

對於中斷響應的問題,從硬件上每個CPU並不相同,可以參考我的這篇文章對ARM7系列的中斷響應進行一個了解。而uCOS對於中斷響應會有一套自己的辦法進行統一處理。

之前分析的內容僅僅是從任務級別來進行分析,但實際上更深入一步分析,中斷的響應,任務的響應都會有一定時間的延時,以圖示為例。

當一個任務運行中得到中斷請求,會首先有一個硬件的響應時間。之后硬件會關閉中斷標志,一般是在CPU的狀態寄存器中的IRQ位置1禁止,隨后跳入中斷入口地址。之后uCOS會進行自己的特殊處理,對中斷進行接管。首先會保存CPU當前寄存器,並通知內核進入中斷函數(一般是調用OSInitEnter函數),之后編寫代碼重新打開中斷,允許中斷嵌套。之后將進入中斷服務函數。這期間的時間就是中斷響應時間。

當服務函數結束后,會調用OSInitExit進行任務調度。若此時有新的任務置於就緒狀態並且優先級比原任務高,則將會進行上下文切換,恢復CPU寄存器,中斷返回后進入新的任務執行。若沒有心的高優先級任務,則會原路返回,回到原來的任務繼續執行。

可以很明顯的看到,經過uCOS接受的中斷響應將變慢,但是由於這樣能實現中斷嵌套和豐富的任務調度,所以這樣的中斷延時是可以接受的。

@.[FIN]      @.date->Apr 1, 2013      @.author->apollius


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM