源碼解讀·RT-Thread多任務調度算法

*本文依據RT-Thread當時最新版本4.0.1版本源碼算法

RT-Thread操做系統是一款基於優先級和時間片輪轉的多任務實時操做系統。其調度算法採用256個優先級,並支持相同優先級的任務存在。不一樣優先級的任務採用優先級調度,而相同優先級的任務則採用時間片輪轉調度。其實這種調度算法在絕大多數系統中都同樣,像我知道的μCos和freertos都是如此。不過這裏須要先了解一個問題,也是我初學時被困擾的問題——多種調度算法存在時那麼什麼時候採用何種調度算法?彼此又是如何共存和協調進行的?這要等看完並看懂調度算法的源碼以後纔算明白其中原理。其實調度算法採用優先級調度爲主要依據,以時間片輪轉爲次要依據。也就是說只當沒有更高優先級任務就緒的狀況下,想同優先級任務之間的調度纔會採用時間片輪轉調度。數組

優先級調度

優先級在多任務調度中是什麼?優先級實際上是給任務分配的一個數值,數值越小則優先級越高。優先級的高低將直接反應在任務調度算法中,優先級越高越優先響應。函數

在RT-Thread中優先級調度算法支持256個優先級,能夠經過宏定義配置。一般狀況下裁剪過程當中會根據須要來定義優先級數量。不過在源碼中只會體現出兩種不一樣優先級數量的差別,分別爲32個和32個以上。RT-Thread採用bitmap算法來計算優先級。bitmap算法是二進制與位運算完美結合的體現。看懂代碼以後相信你們都會來一句「臥槽,既然還能夠這樣操做!」,真的崇拜發明bitmap算法的大佬,不過我並不知道是誰最早發明的,第一次接觸是在學習μCos的時候。學習

前面提到過,RT-Thread根據優先級的數量不一樣分爲兩種bitmap算法(優先級32個和32個以上),代碼稍微有些差別,主要是爲了優化資源佔用。其中不超過32個優先級的狀況下只會用一個32bit的變量,超過32個優先級後會使用一個長度爲32個元素的byte數組,外加一個32bit的變量用來分組。其中不管多少個優先級,每一個優先級都只須要用一個bit來表示對應優先級的任務是否就緒狀態(爲1表示就緒,爲0表示掛起),因此最多支持256個優先級。優化

bitmap算法

爲了理解優先級計算中使用的bitmap算法,首先必需要先掌握十進制與二進制的轉換,而且還須要掌握位運算。以RT-Thread中優先級大於32個的狀況爲例來講明,其32個及一下優先級數量的方式更簡單,稍後會簡單說明。先看RT-Thread中的源碼,關於bitmap算法會用到的幾個變量:ui

rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
struct rt_thread *rt_current_thread;
rt_uint8_t rt_current_priority;

#if RT_THREAD_PRIORITY_MAX > 32
    /* Maximum priority level, 256 */
    rt_uint32_t rt_thread_ready_priority_group;
    rt_uint8_trt_thread_ready_table[32];
#else
    /* Maximum priority level, 32 */
    rt_uint32_t rt_thread_ready_priority_group;
#endif

 

RT-Thread中當優先級大於32個的狀況下,將任務優先級分爲32組,每組8個。其分組用rt_thread_ready_priority_group變量來管理,這是一個32bit的變量,每個bit表明一個組的就緒態。而每一組中的8個優先級則用rt_thread_ready_table數組來管理,其每一個元素佔用一個8bit大小,一樣每一個bit表明一個優先級。在源碼中優先級從0開始最大到255,因此具體的分組狀況就是每8個爲一組用一個bit來表示,依次分爲32組共須要32個bit來表示分組,也就是rt_thread_ready_priority_group變量。當某一組中對應的優先級任務處於就緒態則對應的bit將被置1.例如優先級爲19的任務處於就緒,則根據分組狀況其處於第三組(16至23),因此rt_thread_ready_priority_group中的第三bit將置1(第三個bit也就是bit2,由於一般習慣將bit從0開始數).這就在後續的調度算法過程當中調度器知道此組中有任務處於就緒了。更進一步的細節是,還須要在rt_thread_ready_table中第三個元素(下標爲2)中的第四bit(bit3)置1(由於19在第三組16,17,18,19中位於第四個).這樣就準確的標識了惟一的優先級號。spa

那若是同時有多個組中的任務都處於就緒該怎麼計算一個最高優先級呢?若是存在同時處於就緒的任務則對應的分組bit都會在rt_thread_ready_priority_group中置1,且同時在rt_thread_ready_table中對應的組中的bit也會置1.前面提到過優先級越高其優先級編號越小,好比優先級0是最高的優先級,優先級255是最低優先級。這就能夠推理得知越小的優先級編號就對應在rt_thread_ready_priority_group越低的bit上。不難看出0至7優先級對應rt_thread_ready_priority_group變量的bit0,同理8至15對應比bit2,16至23對應bit3.等等。現假設優先級5和19的任務處於就緒態,那麼其rt_thread_ready_priority_group變量的bit0和bit2將被置1,同時rt_thread_ready_table[0]的bit5以及rt_thread_ready_table[2]的bit3將被置1.以下圖所示: 操作系統

 

 

 

若是此時調度器調度時,應該要計算出優先級爲5的任務來執行。這其實分了三步來計算的。code

首先拿rt_thread_ready_priority_group變量利用ffs函數計算最低位爲1的bit是第幾個bit。顯然這個例子中是第一個bit0位爲1,假如這個結果咱們用叫index的變量暫存起來,那麼index等於1.blog

第二步利用第一步計算的結果index做爲rt_thread_ready_table的索引(索引從0開始爲第一個,因此要index-1),即rt_thread_ready_table[0]。再一次作ffs(rt_thread_ready_table[0])計算最低位爲1的bit是第幾位,顯然例子是bit5,假如咱們再用個變量offset存儲這個值,那麼offset等於6。

第三步根據前兩步計算出來的index和offset得出最終的最高優先級爲(index-1)*8+(offset-1),這裏的乘法能夠用位運算代替,因此等價於((index-1) <<3) + (offset-1).其真正的RT-Thread源碼以下:

register rt_ubase_t highest_ready_priority;

#if RT_THREAD_PRIORITY_MAX <= 32
    highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;

#else
    register rt_ubase_t number;

    number = __rt_ffs(rt_thread_ready_priority_group) -1;
    highest_ready_priority = (number << 3) +__rt_ffs(rt_thread_ready_table[number]) - 1;
#endif

 

其上面的代碼__rt_ffs返回值就如上例的index變量,作了減1操做是由於索引和bit都從0開始。highest_ready_priority即計算出來的最高優先級。

關於ffs函數的實現細節能夠看RT-Thread源碼裏的各類實現方式,有C函數實現,也有針對各類編譯器和處理器優化的特殊指令的實現。不過其功能就是計算一個值二進制位爲1的最低位是第幾位。在RT-Thread的C函數實現中作了一個0到255的索引數組,其數組的值分別就是0到255這些數值所對應的二進制位爲1的最低位索引。

最後說明一下,如上面的代碼所示,當優先級數定義爲不超過32個時,就不存在rt_thread_ready_table了,更節省資源。也能夠理解爲每組只有一個優先級,因此能夠直接用rt_thread_ready_priority_group直接代替了。由於最多才32個優先級,rt_thread_ready_priority_group恰好32bit,每一個bit表明一個優先級恰好對應上。

時間片輪轉調度

在說明時間片輪轉調度前,先要說明一下什麼是時間片。在操做系統裏,時間片的概念是相對於操做系統的TICK中斷的。每觸發一次TICK中斷就至關於一個時間片。

時間片輪轉調度會在每一個TICK中斷時對當前任務的時間片減一,而後檢查其它任務的時間片剩餘狀況。一旦當前任務的時間片用完,則會先重置當前任務的時間片。而後看是否有想同優先級的任務,若是有則會將當前任務移到隊列末尾。而後觸發優先級調度,此時只要當前優先級是已就緒的最高優先級最終就會取出相同優先級隊列頭的任務運行。拋開其它因素簡單來講就是隻要當前任務的時間片用完了,則會將當前任務移到隊列末尾,下一個任務天然而然處於隊列頭將得到運行。因此這就看起來是每一個任務輪流來運行,只是每一個任務的運行時間長短不同而已,這個運行的時間長短就是由時間片指定的。

綜上所述,體現時間片輪轉調度的前提是創建多個相同優先級的任務。由於時間片輪轉調度只會發生在相同優先級的任務之間。不然能夠認爲系統中只存在優先級調度。

下圖展現了三個相同優先級任務的時間片輪轉調度運行狀況:

 

相關文章
相關標籤/搜索