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