rt-thread的線程調(diào)度與管理
掃描二維碼
隨時隨地手機看文章
rt-thread的線程調(diào)度與管理
-
?1.調(diào)度是什么?
-
?2.調(diào)度怎么實現(xiàn)?
-
?3.什么時候系統(tǒng)做調(diào)度?
-
?3.1 任務(wù)主動block
-
?3.2 被更高優(yōu)先級的任務(wù)喚醒
-
?3.3 yield放棄cpu使用
-
?3.4 中斷中執(zhí)行調(diào)度
-
?4.調(diào)度做了哪些事情?
-
?5.總結(jié)
要想使用好rtos,做出更加穩(wěn)定可靠的產(chǎn)品,必須非常清楚底層的調(diào)度原理。由于RTOS的可控性,所以只有了解了其核心部分的設(shè)計思路,才能用起來得心應(yīng)手,游刃有余。本文主要是聽完熊大對rt-thread調(diào)度講解之后,自己做了一些反思總結(jié),打算分享一下rt-thread線程的調(diào)度與管理相關(guān)的比較核心和重要的部分的筆記。
1.調(diào)度是什么?
調(diào)度一般就是合理的安排,協(xié)調(diào)資源,統(tǒng)一指揮去完成一件事,而在操作系統(tǒng)中,線程調(diào)度就是有多個就緒優(yōu)先級的任務(wù),找到最高優(yōu)先級任務(wù),交給CPU去運行。
rt-thread調(diào)度器就是起到判決線程當前的優(yōu)先級,然后去執(zhí)行當前最高優(yōu)先級的就緒的線程。
調(diào)度又可以細分為兩種??纱驍嗾{(diào)度:關(guān)鍵防止優(yōu)先級倒置 ;不可打斷調(diào)度:先來先服務(wù),不可中斷。
2.調(diào)度怎么實現(xiàn)?
在創(chuàng)建任務(wù)的時候,指定了任務(wù)的優(yōu)先級,一般來說,每個任務(wù)都有自己特定的優(yōu)先級。所以內(nèi)核線程對象中有不同的優(yōu)先級的任務(wù)列表。
如果最大指定為32個優(yōu)先級,那么可以用u32,每一個bit表示一個優(yōu)先級就緒的狀態(tài)。使用位圖的優(yōu)點就是速度快,而且內(nèi)存占用小。
一般來說,調(diào)度去找到最高優(yōu)先級的任務(wù)時,就需要去做判斷。如何去找到最高優(yōu)先級的任務(wù)。一般來說,有兩種辦法:
- 軟件計算
- 硬件計算
這兩種的差別僅僅在于計算效率的問題,本質(zhì)目的并無差別。
而尋找最高優(yōu)先級的事情也是有兩種實現(xiàn)的策略:
1.遍歷就緒的隊列,找到最小的就緒的隊列,尋找的時間不確定,時間復雜度O(n)。
2.采用空間換時間的辦法,事先做好一個bitmap
例如系統(tǒng)中最大有8個優(yōu)先級,那么bitmap如下:
const rt_uint8_t __lowest_bit_bitmap[] =
{
/* 00 */ 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* 10 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* 20 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* 30 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* 40 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* 50 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* 60 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* 70 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* 80 */ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* 90 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* A0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* B0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* C0 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* D0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* E0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
/* F0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};
一般每一位代表一個就緒的狀態(tài),所以__rt_ffs程序的設(shè)計如下
int __rt_ffs(int value)
{ if (value == 0) return 0; if (value & 0xff) return __lowest_bit_bitmap[value & 0xff] + 1; if (value & 0xff00) return __lowest_bit_bitmap[(value & 0xff00) >> 8] + 9; if (value & 0xff0000) return __lowest_bit_bitmap[(value & 0xff0000) >> 16] + 17; return __lowest_bit_bitmap[(value & 0xff000000) >> 24] + 25;
}
如果當前系統(tǒng)的線程狀態(tài)為0b0110 0000,那么轉(zhuǎn)換成十六進制就是0x60,根據(jù)表中的狀態(tài)此時的最高優(yōu)先級是5+1=6。所以可以得出系統(tǒng)的優(yōu)先級,此時計算的復雜度為O(1)。
雖然rtt是支持同等優(yōu)先級的,但是在具體的業(yè)務(wù)邏輯的設(shè)計中,在使用RTOS常用的設(shè)計方法中,一般都是要求程序的運行邏輯是可預(yù)測的,就是在程序執(zhí)行的過程中,可以預(yù)測到程序下一步的動作。所以rtos中同等優(yōu)先級,按照時間片輪訓的這種方式設(shè)計業(yè)務(wù)邏輯的情況并不多。使用相同優(yōu)先級會增加系統(tǒng)的業(yè)務(wù)邏輯的復雜性。
3.什么時候系統(tǒng)做調(diào)度?
RTT是搶占式的系統(tǒng)調(diào)用,所以系統(tǒng)什么時候去做的調(diào)度非常的關(guān)鍵。系統(tǒng)調(diào)度分為主動調(diào)度和被動兩種。
3.1 任務(wù)主動block
當A線程在正常運行時,主動放棄CPU的使用權(quán),比如去執(zhí)行rt_thread_delay或者去等待一個IPC的事件到來時,都會釋放CPU進行調(diào)度,此時去系統(tǒng)中尋找已經(jīng)就緒的最高優(yōu)先級的線程進行調(diào)度。
這種方式應(yīng)用的場景比較豐富,比如當前線程沒有獲取到資源時,需讓出CPU的使用權(quán),或者事情做完了,主動讓出CPU的使用權(quán),這就是系統(tǒng)做調(diào)度的時機。
A線程的優(yōu)先級要高于B線程的優(yōu)先級,所以在A放棄CPU使用權(quán)后,已經(jīng)就緒的最高優(yōu)先級線程B就開始執(zhí)行了。
3.2 被更高優(yōu)先級的任務(wù)喚醒
這種方式就是當比當前運行線程的優(yōu)先級高的線程處于就緒態(tài)時,會調(diào)度到比當前線程更高的優(yōu)先級線程中去。
按照理解A線程是正在運行的線程,此時更高任務(wù)優(yōu)先級的線程C就緒處于就緒狀態(tài)了,所以系統(tǒng)的tick函數(shù)中判斷已經(jīng)有比線程A更高優(yōu)先級的線程處于就緒狀態(tài),于是執(zhí)行了rt_schedule()函數(shù)執(zhí)行了系統(tǒng)調(diào)度。當前A線程運行狀態(tài)壓棧,更高優(yōu)先級的C線程的狀態(tài)出棧,開始運行C線程。
3.3 yield放棄cpu使用
首先理解一下什么是yield,解釋成讓出,放棄比較合理。該出讓只針對于同等優(yōu)先級的線程。
這種情況只適用于A線程的優(yōu)先級等于B線程的優(yōu)先級的情況。因為RTT支持同等優(yōu)先級的方式創(chuàng)建線程,相同的優(yōu)先級的切換是靠時間片輪詢來進行的。所以,當A線程正常運行的時候,如果執(zhí)行了yield函數(shù),那么只相當于將A線程的時間片消耗完,此時同等優(yōu)先級的D線程開始運行。
由于在RTOS中,需要的是完成任務(wù)的確定性與可靠性,同等優(yōu)先級的情況比較有限,所以這一塊應(yīng)用的不多。
3.4 中斷中執(zhí)行調(diào)度
以上的三種屬于主動進行調(diào)度的過程,其系統(tǒng)的執(zhí)行流程都是可以預(yù)測的,但是中斷去執(zhí)行調(diào)度卻是比較特殊。是被動調(diào)度。
這種方式是在中斷中執(zhí)行調(diào)度的,當A線程正常運行時,此時來了一個中斷,由于中斷的優(yōu)先級是高于線程的。所以,中斷處理事情,如果在中斷中執(zhí)行了調(diào)度函數(shù),那么在中斷退出后,將直接切換到當前系統(tǒng)中更高優(yōu)先級的線程去運行。如果如果當前系統(tǒng)的最高優(yōu)先級還是A,那么中斷退出后,執(zhí)行的最高優(yōu)先級線程依然是A。若存在線程E線程優(yōu)先級高于A并且處于就緒狀態(tài),此時,中斷退出后,切換到E線程去執(zhí)行。
4.調(diào)度做了哪些事情?
系統(tǒng)進行調(diào)度的時候做了哪些事情?
第一步:查找當前系統(tǒng)中當前以及就緒的最高優(yōu)先級的線程,若有高于當前運行系統(tǒng)運行的線程棧則執(zhí)行線程切換
第二步:關(guān)閉中斷,將系統(tǒng)當前運行的寄存器壓入棧空間
第三步: 找到需要運行的線程的PC指針,并找到棧起始處彈出棧中的寄存器狀態(tài)
第四部:打開中斷,執(zhí)行異常ret,讓系統(tǒng)恢復執(zhí)行
此時,就切換到已經(jīng)就緒的更高優(yōu)先級的線程去運行了。
5.總結(jié)
rt-thread線程的調(diào)度原理和過程上述文章已經(jīng)寫的比較詳細了,主要需要注意的是調(diào)度器的原理以及調(diào)度的時機的問題。往往在利用rt-thread做具體的項目的時候,需要非常清楚的理解調(diào)度過程,通過閱讀代碼,就能預(yù)測程序下一步的執(zhí)行動作。真正的做到手中有糧,心中不慌。





