uCOS-II中的任務切換-圖解多種任務排程時機與問題
【@.1 任務排程時機】
之前的一篇文章分析了具體的uCOS-II中的任務切換機制,是從函式呼叫的角度上分析的。這次我具體從整個程式執行的時間上來看,分析多種任務排程發生的時機。以下所有圖片均可點選放大觀察。
所有圖中紅色箭頭表示中斷級的任務切換,藍色箭頭表示任務級的中斷切換。
1.僅有一個任務,這種情況最簡單。假設時鐘節拍是1000次每秒,由定時中斷產生,當節拍的時鐘服務程式結束時會呼叫OSInitExit,退出中斷,其中將進行上下文切換,運行當前就緒狀態優先順序最高的任務,這裡當然就是任務A、任務A中的程式碼比較簡單,執行到最後時假設呼叫OSTimeDly(1)延時一個週期,提示系統放棄CPU控制權,這時將進行任務切換到空閒任務Idle Task。空閒任務優先順序最低,是一個死迴圈,僅僅讓一個OSIdleCtr迴圈一次加一。可以看出,在一個時鐘節拍的間隔,這個計數器可能加不止一次。另外uCOS中還有一個統計任務,需配置開啟,其中就是利用這個空閒計數器求出CPU有多少時間在空閒任務中,即有多少CPU佔空比。通過圖示分析可以看出,很明顯雖然任務A延時一個時鐘週期,1ms,但是實際上將會少於1ms的延時。這就是為什麼實際的延時中或多或少都會有延時抖動的現象,下面的很多例子的延時抖動都可能比這種情況更加複雜。
2.簡單的中斷,這裡假設有一個外部中斷,在圖示處打斷了任務A的執行。外部中斷響應後進入服務函式,中斷退出時呼叫OSInitExit進行任務切換,回到剛才被中斷的任務。所以剛才的任務A就被延後了一段時間允許,之後任務A再切換到空閒任務。很明顯,這個時鐘週期內空閒計數器OSIdleCtr會少加一些,最後可以統計出CPU佔用率會上升一些。
3.中斷函式中利用OSSemPost傳送一個訊號量,接受訊號量的是任務B,其優先順序大於任務A。於是在每次中斷服務函式結束時會首先排程任務B執行,按照圖中任務B的程式碼只有一個OSSemPend可能會有等待掛起傳送,之後任務B會因為等待接受訊號量而排程會任務A繼續執行。圖中的畫出了三個外部中斷分別出現在不同時期,其中第三個外部中斷將導致任務B執行到一半遇到時鐘節拍的產生。這時候任務B會掛起,將執行節拍中斷服務程式,經過排程後會發現任務B任然處於就緒狀態,所以將繼續執行任務B直到結束。最後這裡可以看出空閒任務在每個時鐘週期被擠得更少了,所以CPU的利用率更多。
4.跟第三中情況類似,只不過這裡任務B中加了一句OSTimeDly(3)延時3個時鐘週期,這時可以知道,在任務B延時期間,只剩TaskA和空閒任務,他們將不會因為任務B的延時而被掛起。延時結束時,時鐘節拍將首先排程延時結束處於就緒狀態的任務B,之後再執行原本的任務A。這種情況也簡單的表示了uCOS是怎樣充分利用CPU資源的。
5.中斷中呼叫OSTaskResume恢復任務B。這裡只是想說明,Resume/Suspend跟Post/Pend的區別,前者是一旦恢復則一直執行,而後者是Post一次,Pend方執行一次。
6.當用訊號量通訊時,利用任務A來發送,控制高優先順序的任務B接受訊號量。可以看出,任務A傳送一次訊號量將會時任務B排程執行,此時任務A由於優先順序低,被掛起,當任務B執行一次迴圈後又Pend等待訊號量,這時任務B被掛起,由下一個時間週期的任務A傳送,周而復始。
7.對於共享資源,可以採用圖示方式編寫。這種方式表明,當兩個任務想要使用共享資源時,每個任務用Pend和Post的組合包圍住想要操作的資源(比如一個函式,一個公共變數)。其中一個任務用完資源後執行Post,將會使另一個Pend等待資源的任務得以往下執行,不論二者的優先順序如何。圖中的任務也包含了每次時鐘節拍對任務的延時作用。不過像這種訊號量通訊的場合會涉及到優先順序反轉問題,我們後面會分析。
8.這裡演示了一個比較惡劣的中斷巢狀例項,可以看出所有任務/中斷級的任務排程只有等最後一層中斷巢狀執行結束後才能執行,在中斷巢狀中若使用Post或Resume想要執行任務排程也只能等到巢狀結束才能執行。uCOS-II支援中斷巢狀,不用擔心中斷的響應問題,但是一旦中斷巢狀之後就會出現很多延時的問題,將會導致整個系統的實時性下降,比如圖示的TaskA就因為中斷巢狀,並且因為中斷巢狀結束後TaskB接受到訊號量而執行,使得其延時效果大大超出我們想象,而且中間還缺了一步時鐘週期,甚至導致這個週期滿負荷(空閒任務不會執行到)。雖然畫的有點誇張,但是這不得不引起我們的注意。所以通常推薦中斷服務函式寫的越短越好,一個好的方法是,清除了中斷標誌後僅僅傳送一個訊號量通知別的任務執行,將所有需要花時間執行的工作交給任務而不是留給中斷服務函式處理。
以上的分析包含了少量幾個任務和間可能出現的常見情況,對於大型程式來說,任務的排程和中斷的響應可能比這個更加複雜,需要更進一步分析。
【@.2 優先順序反轉】
圖示的情況將出現優先順序反轉。任務A和C需要處理一個共享資源,低優先順序的任務C首先得到訊號量,處理完成後Post傳送訊號量通知等待中的任務A得以執行,任務A處理完共享訊號量後Post,將使得等待中的任務C執行,而過了一段時間,任務B由於某種原因被恢復為就緒狀態,則任務B會搶奪任務C的CPU使用權,待任務B結束後任務C才得以進行,之後任務C傳送訊號量,通知任務A得以繼續執行。
這將會使得任務A等待訊號量的時間延長,看上去是優先順序低於A的任務B先於任務A執行,這往往也是我們所不希望的。這個問題的原因就是因為任務C的優先順序太低,所以解決這個問題時簡單的方法就是動態修改進行共享資源操作的任務的優先順序即可。於是引入了互斥訊號量。
互斥訊號量跟普通訊號量基本一致,區別在於通過Pend得到訊號量的任務將會被動態修改其優先順序。這裡我們一般會設定一個比較高的優先順序,如圖所示,任務A和任務C交替得到訊號量,並且得到訊號量時被賦予了一個更高的優先順序。這時若任務B進入就緒狀態就無法得以運行了。注意到這僅僅是uCOS-II的解決方法,因為uCOS-II不支援多工同一優先順序。
【@.3 uCOS中的中斷延時】
對於中斷響應的問題,從硬體上每個CPU並不相同,可以參考我的這篇文章對ARM7系列的中斷響應進行一個瞭解。而uCOS對於中斷響應會有一套自己的辦法進行統一處理。
之前分析的內容僅僅是從任務級別來進行分析,但實際上更深入一步分析,中斷的響應,任務的響應都會有一定時間的延時,以圖示為例。
當一個任務執行中得到中斷請求,會首先有一個硬體的響應時間。之後硬體會關閉中斷標誌,一般是在CPU的狀態暫存器中的IRQ位置1禁止,隨後跳入中斷入口地址。之後uCOS會進行自己的特殊處理,對中斷進行接管。首先會儲存CPU當前暫存器,並通知核心進入中斷函式(一般是呼叫OSInitEnter函式),之後編寫程式碼重新開啟中斷,允許中斷巢狀。之後將進入中斷服務函式。這期間的時間就是中斷響應時間。
當服務函式結束後,會呼叫OSInitExit進行任務排程。若此時有新的任務置於就緒狀態並且優先順序比原任務高,則將會進行上下文切換,恢復CPU暫存器,中斷返回後進入新的任務執行。若沒有心的高優先順序任務,則會原路返回,回到原來的任務繼續執行。
可以很明顯的看到,經過uCOS接受的中斷響應將變慢,但是由於這樣能實現中斷巢狀和豐富的任務排程,所以這樣的中斷延時是可以接受的。
@.[FIN] @.date->Apr 1, 2013 @.author->apollius