五 .調度

調度的定義:

調度器,決定了任務的運行順序。uC/OS-III 是一個可搶佔的,基於優先級的內核。根據其重要性每個任務都被分配了一個優先級。uC/OS-III 支持多個任務擁有相同的優先級。
“可搶佔的”意味當事件發生時,如果事件讓高優先級任務被就緒,uC/OS-III 馬上將CPU 的控制權交給高優先級任務。因此,當一個任務提交信號量、發送消息給一個高優先級的任務(若該任務被就緒了),當前的任務就會被停止,更高優先級的任務獲得CPU 的控制權。類似的,當ISR 提交信號量或發送消息給一更高優先級的任務(若該任務被就緒了),那麼中斷返回的時候不會返回到原任務,而是高優先級任務。

一  搶佔式調度

uC/OS-III 通過兩種方法處理中斷提交的事件:直接提交或延遲提交。

其實上圖就是一個基本的中斷控制系統的操作。進程如下
(1)一個低優先級任務正在執行,這時中斷髮生了。
(2)如果中斷使能,指令指針IP 會跳轉到中斷服務程序。
(3)中斷服務程序處理相關的程序,發送信號量或消息給一個高優先級任務。高優先級任務被就緒。
(4)中斷服務程序完成操作後,它將CPU 的控制權還給uC/OS-III。
(5)uC/OS-III 執行相應的操作。
(6)因爲就緒隊列中有一個更重要的任務,uC/OS-III 將不會返回中斷前那個低優先級的任務。而是執行高優先級任務。
(7)開始執行高優先級任務
(8)高優先級任務處理完成後,將CPU 的控制權交給uC/OS-III。
(9)uC/OS-III 執行相應的操作。
(10)uC/OS-III 轉向執行原先那個低優先級任務。
(11)低優先級任務從被中斷處繼續執行。
圖7-2 顯示了當選擇延遲提交中斷產生的事件的方式時的一些額外步驟。最後的結果是一樣的:高優先級任務搶佔了低優先級任務。


(1)中斷服務程序中,不是直接發送信號量或消息給任務。而是先將信號量或消息放人外部消息隊列,並就緒中斷處理任務。
(2)當ISR 處理完它的工作時,就把CPU 的使用權交給uC/OS-III。
(3)uC/OS-III 處理一些操作。
(4)因爲中斷處理任務被就緒,uC/OS-III 將CPU 的使用權交給它。
(5)uC/OS-III 處理一些操作。
(6) 中斷處理任務將外部消息隊列中的消息移除並重新提交給相應的任務。這樣,就將這個過程消息提交從中斷級變成了任務級。使用這種方法的目的是爲了減小關中斷時間(詳見第9 章)。當消息隊列爲空時,uC/OS-III 將中斷處理任務從就緒隊列中移除,然後執行就緒隊列中的最高優先級任務。

二調度點

當出現以下情況時會發生調度:
1 任務被標記或發送消息給另一個任務
任務調用提交服務函數OS???Post(),發送信號量或消息給其它任務時調度發生。調度在OS???Post()函數的結束時發生。注意在有些情況下,調度是不會發生的(見OS_OPT_POST_NO_SCHED 的可選參數)。
2 任務調用 OSTimeDly()或OSTimeDlyHMSM():
如果延時參數不是0,調度發生,調度會在該任務被放入掛起隊列後馬上執行。
3 任務所等待的事件發生或超時
當OS???Pend()被調用時。接收到該事件的任務、或超時的任務就會被移出等待隊列。然後調度器選擇就緒列表中優先級最高的任務執行。{移出等待隊列的任務不一定就是就緒狀態,因爲它還可能在停止隊列中,效果是可以疊加的。}
4 任務取消掛起
任務可以被取消掛起,若另一個任務調用OS???PendAbort()。當任務被移出等待列表中時調度發生。
5 新任務被創建
新的任務被創建時調度發生。
6 任務被刪除
當一個任務被刪除時調度發生。
7 內核對象被刪除
任務所等待的內核對象被刪除時(事件標誌組、信號量、消息隊列、mutex 都是內核對象),這些任務就可能被就緒。然後調度發生。
8 任務改變自身的優先級或其它任務的優先級
當任務改變自身優先級或其它任務優先級時,調度發生。
9 當任務通過調用 OSTaskSuspend()停止自身
當任務調用OSTaskSuspend()停止自身時,調度發生。
10 任務調用 OSTaskResume()恢復其它停止了的任務
任務調用OSTaskResume()恢復其它停止了的任務時,調度發生。
11 退出中斷服務程序
當退出中斷服務程序時,調度發生。這種情況下,調度器調用OSIntExit()函數開始調度而不是OSSched()。
12 通過調用 OSSchedUnlock()調度器被解鎖
調用OSSchedLock()鎖調度器,調度發生。需要注意的是,鎖調度器可以被嵌套,解鎖次數必須等於加鎖次數。
13 調用OSSchedRoundRobinYield()任務放棄了分配給它的時間片
假定多個任務有相同的優先級,正在運行的任務放棄了分配給它的時間片。調度發生。
14 用戶調用 OSSched()
用戶程序調用OSSched()時,調度發生。若調用OS???Post()函數時設置參數爲了OS_OPT_POST_NO_SCHED,事件在被提交後,不調用調度器。當然,任務可以一次性提交多個事件,但在最後一個事件提交時才調用調度器。{因爲當一個事件被提交時,調度器就會發生調度。所以用戶設置了OS_OPT_POST_NO_SCHED 後,用戶一次性提交多個事件而只發生一次發生調度。}

三  循環輪轉調度

當多個任務有相同的優先級時,uC/OS-III 允許每個任務運行規定的時間片。當任務沒有用完分配給它的時間片時,它可以自願地放棄CPU。uC/OS-III 允許任務在運行時開啓或者關閉循環輪轉調度。圖F7-3 是相同優先級下任務運行的時序圖。如圖,有三個優先級都爲X 的任務。爲了說明,時間片的長度爲4。

(1)任務3 正在被運行。在這段時間內,時基中斷髮生,但任務3 還沒有到期。
(2)任務3 主動放棄剩下的時間片。
(3)uC/OS-III 恢復任務1,因爲它是隊列中任務3 的下一個任務。
(4)任務1 被執行直到分配給它的時間片到期。
(5)任務3 正在被運行。在這段時間內,時基中斷髮生,但任務3 還沒有到期。
(6)任務3 主動放棄剩下的時間片。
(7)有趣的事情發生了,uC/OS-III 會重新設置任務1 的時間片長度爲4 個時基。但剩餘時間片的計數是每個時基中斷遞減,即在第4 個時基中斷髮生時時間片到期。而任務1 是在時基即將發生時接手的,所以事實上它的時間片會少一個時基。
(8)任務1 執行,uC/OS-III 允許用戶在任務運行時修改時間片的長度(通過OSSchedRoundRobinCfg(),)。這個函數也可以用於開啓或關閉循環輪轉調度。
uC/OS-III 允許用戶爲每個任務設置不同的時間片。不同的任務可以有不同的時間片。當任務被創建時,其時間片長度被設置。也可以在運行時調OSTaskTimeQuantaSet()修改。

四  調度的內部實現

是通過這兩個函數完成調度功能:OSSched()和OSintExit()
OSSched()是在任務級被調用,OSIntExit()是在中斷級被調用,這兩個函數都是在os.core.c中定義
如下圖,闡述了調度所需要的兩個數據結構:位映像表和就緒列表



(1)任務級調度OSSched()

這是一個任務級調度的僞代碼

1)因爲要進入調度程序,關中斷。OSSched()首先確定自己不是被中斷服務程序調用,因爲它是任務級調度程序。中斷級調度需調用OSIntExit()。
2)第二步是確保調度器沒有被鎖。如果調度器被鎖,就不能運行調度器,函數馬上返回。
3)OSSched()通過掃描位映像表OSPrioTbl[]找到就緒列表中優先級最高的位。(詳見章節6)
4)確定好優先級後,索引到OSRdyList[]並提取該記錄中的首個TCB(OSRdyList[highest priority].HeadPtr)。到現在爲止,已經知道了哪個任務將要切換爲運行狀態。特別的,OSTCBCurPtr 指向的是當前任務的TCB,OSTCBHighRdyPtr 指向的是將要被切換的TCB。
5)當就緒表中的最高優先級任務不爲當前正在運行的任務時(OSTCBCurPtr!=OSTCBHighRdyPtr),進行任務級的上下文切換。然後在開中斷。
注意的是,調度時和上下文切換是運行在關中斷的狀態下的。這是必要的因爲這些操作是原子性的。
2.中斷級調度 OSIntExit()

這是一箇中斷級調度的僞代碼
1)OSIntExit()開始時確保中斷嵌套級不爲0。{如果爲0 的話,那麼接下來的OSIntNestingCtr--就會發生溢出了}
2)OSInrExit()將OSIntNestingCtr 遞減,若OSIntNestingCtr 不爲0 時則返回。確保該函數是在第一級ISR 中執行。當還有中斷程序未被處理時就不能運行調度器。
3)OSIntExit()檢查調度器是否被鎖。如果被鎖,OSIntExit()不會運行調度器並返回到中斷前的任務。
4)當這是第一級中斷且調度器沒有被鎖時。查找優先級最高的任務。
5)從OSRdyList[]中獲得最高優先級的TCB。
6)如果最高優先級就緒任務不是當前正在運行的任務。uC/OS-III 就執行中斷級的上下文切換。中斷級切換的上文已經在中斷開始時被保存了,它可以直接切換到任務。這些在第8 章中詳細介紹。






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