手把手,嘴對嘴,講解UCOSII嵌入式操作系統的任務調度策略(二)


 

 繼續……

if (ticks > 0u) {                            /* 延時參數是否為0 */
        OS_ENTER_CRITICAL();            /* 禁止中斷 */
        y            =  OSTCBCur->OSTCBY;
        OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;
        if (OSRdyTbl[y] == 0u) {
            OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;
        }
        OSTCBCur->OSTCBDly = ticks;
        OS_EXIT_CRITICAL();              /* 開啟中斷 */
        OS_Sched();
    }

在上一段代碼中,出現了一個陌生的數組: OSRdyTbl[],跟蹤這個變量可以找到它的定義,發現它僅僅是一個uint8型的數組,長度是8個。

可以明確的告訴大家,這個數組很重要,應該算是任務優先級調度核心參數之一,與下面那個參數OSRdyGrp 合起來便可以作為任務就緒表

※接下來需要講UCOSII系統的任務優先級調度策略,這一段有些復雜,需要反復思考,查閱大量的資料。

UCOSII的優先級策略

UCOSII操作系統最大可以管理64個任務(255個的暫時不討論),每個任務都有唯一的優先級,從0開始到64,數字越小優先級越高,越優先進行系統調用,為了方面管理和調度,系統把64任務的優先級進行了分組。

每8個優先級為一組,一共8組。

舉例:

  以前我上幼兒園的時候,我們小班有64個小朋友,每個小朋友都有自己的學號(從0到63),老師為了方面管理,把我們分成了8個小組(從0到7),每組8個小朋友……作為小朋友的我來說,當然不理解這樣分組的意義,不過對於老師而言,這樣做肯定是有用的。

       下面是分組以后小朋友的坐席表:

  

 

 

當有一個名叫波波的小朋友的學號是12的時候,那他屬於哪個組?看下圖,當然是第1組,而且我的座位號排第5(8,9,10,11,12

  

 

當一個任務的優先級確定了以后,那么它的組號和在組內的坐席號都是確定的,是永遠不可能發生改變的,所以在任務創建之時,這些信息都可以完全決定下來,現在再回想那兩句代碼,意義是不是瞬間就明白了?

當一個任務被創建以后,他所屬的組號必然就等於優先級的二級制的高3位,比如……下圖:

根據上面那個圖,那么這兩句代碼的意義也就很清楚了:

        ptcb->OSTCBY             = (INT8U)(prio >> 3u);
        ptcb->OSTCBX             = (INT8U)(prio & 0x07u);

就是根據任務的優先級,計算出組號組內編號

至於這兩個數據有什么用,分組出來有什么意義?請在小朋友波波的帶領下繼續往下看。

剛才那個陌生的數組: OSRdyTbl[8]之所以被稱為任務就緒表,是因為它里面保存的是當前所有任務的就緒狀態,它的長度是8,每一個元素代表一個組,比如 OSRdyTbl[0]代表第0組, OSRdyTbl[1]代表第1組,OSRdyTbl[2]代表第2組……以此類推。

由於它的數據類型是數據uint8,所以每一個元素中的每一個位(bit)代表組內的任務的就緒狀態(1為就緒,0為未就緒)

舉例:

  比如,當優先級為12 的任務就緒時,那么對應的OSRdyTbl[1]的第4位bit,絕對等於1……當整個系統中,當只有優先級為12的任務就緒,其他所有任務都沒有就緒時,那么OSRdyTbl[1] 絕對等於0x10。

  再比如,當優先級為0 的任務就緒時,那么對應的OSRdyTbl[0]的第0位bit,絕對等於1……當整個系統中,當只有優先級為0的任務就緒,其他所有任務都沒有就緒時,那么OSRdyTbl[0] 絕對等於0x01。

       再再比如,當優先級為63 的任務就緒時,那么對應的OSRdyTbl[7]的第8位bit,絕對等於1……當整個系統中,當只有優先級為63的任務就緒,其他所有任務都沒有就緒時,那么OSRdyTbl[7] 絕對等於0x80。

       再再再比如,當優先級為0和1的任務就緒時,那么對應的OSRdyTbl[0]的第0位bit以及第1位bit,都絕對等於1……當整個系統中,當只有優先級為0和1的任務就緒,其他所有任務都沒有就緒時,那么OSRdyTbl[0] 絕對等於0x03。

小朋友們,現在理解了嗎?上面的話要認真理解,理解完后才可以繼續往下看。

 

當一個任務進入延時函數后,這個任務首先要暫停/休眠(把自己的就緒狀態取消),再要把CPU的執行權交給別的任務(把別的任務設置為就緒),這個過程也就是任務的切換。

然后現在我們可以繼續讀代碼了:

    if (ticks > 0u) {                            /* 0 means no delay!                                  */
        OS_ENTER_CRITICAL();
        y            = OSTCBCur->OSTCBY; /* Delay current task */
        OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;
        if (OSRdyTbl[y] == 0u) {
            OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;
        }
        OSTCBCur->OSTCBDly = ticks;              /* Load ticks in TCB                                  */
        OS_EXIT_CRITICAL();
        OS_Sched();                              /* Find next task to run!                             */
    }

紅色的那句代碼所表達的意思,就是把當前任務的就緒表從1設置為0,從而使自身進入休眠狀態。

當優先級為12的任務進入此地,那么OSTCBCur->OSTCBY和y必然等於1,OSTCBCur->OSTCBBitX必然等於0x10(他的座位號是第4),OSRdyTbl[1]在執行紅色代碼之前,肯定是等於0x10(假如當前只有12這一個就緒任務)。

經過這樣一個取反在相與的計算,直接就把OSRdyTbl[1]的第4位bit的清空了,也就是把就緒狀態,設置成了未就緒狀態。

    if (ticks > 0u) {                            /* 0 means no delay!                                  */
        OS_ENTER_CRITICAL();
        y            =  OSTCBCur->OSTCBY;        /* Delay current task                                 */
        OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;
        if (OSRdyTbl[y] == 0u) {
            OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY; }
        OSTCBCur->OSTCBDly = ticks;              /* Load ticks in TCB                                  */
        OS_EXIT_CRITICAL();
        OS_Sched();                              /* Find next task to run!                             */
    }

  這一句代碼,表示什么意思呢?

  OSRdyTbl[]數組中的每個元素都代表了8個優先級任務的狀態,按照字面意思解讀,如果OSRdyTbl[x] == 0x00  (二進制00000000),那么就表示,當前這個組里面,沒有任何就緒的任務。

  跟蹤變量OSRdyGrp ,可以找到它就是一個uint8型的數據,那么OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;這代碼本身的意思無需多言,也就是清空這個數據的某個bit。

  OSRdyGrp 這個變量的作用是管理任務就緒組的組號,原理和OSRdyTbl[]數組差不多,這個擁有8個bit的變量,每一個bit代表一個組,只要這個組內有任何一個任務就緒,那么這個變量對應的bit位,就會置為1,只有該組內所有的任務都是未就緒的狀態,對應的那個bit才會被清零,邏輯算法上是的關系。

  舉例:

    比如,系統中只有任務0就緒了,那么OSRdyGrp 便等於 0x01(二進制00000001)。

    比如,系統中只有任務12就緒了,那么OSRdyGrp 便等於 0x02(二進制00000010)。

    比如,系統中只有任務63就緒了,那么OSRdyGrp 便等於 0x80(二進制10000000)。

         比如,系統中有任務0和任務1就緒了,那么OSRdyGrp 便等於 0x01(二進制00000001)。

    比如,系統中有任務0和任務63都就緒了,那么OSRdyGrp 便等於 0x81(二進制10000001)。

回到代碼,因為同一個組內的所有任務都是邏輯或的關系,所以在同一個組內的所有任務都沒有就緒的情況下,才能把這個變量的對應bit位清空,只要有一個任務就緒,哪怕其余7個任務都沒有就緒,也不能清空,這個if實現的就是這個功能。

總結一下這四個變量的意義:

 

ptcb->OSTCBY    :當前任務優先級所屬於的分組(0~7),比如優先級為12的任務,這個變量應該就是1

ptcb->OSTCBX    :當前任務優先級在組內的序號,比如優先級為12的任務,這個變量應該就是4

ptcb->OSTCBBitY     :用於邏輯運算的二進制變量,當前任務優先級所屬於的組號,在變量OSRdyGrp 中的所占的bit的偏移,比如優先級為12的任務,組號排在第一個位置,那么這個變量應該就是二進制00000010

ptcb->OSTCBBitX     :用於邏輯運算的二進制變量,當前任務優先級在組內的序號所占的bit偏移,比如優先級為12的任務,排在第五個位置,因此這個變量應該就是二進制00010000

現在理解這些變量的意義了嗎?

 

 

    if (ticks > 0u) {                            /* 0 means no delay!                                  */
        OS_ENTER_CRITICAL();
        y            =  OSTCBCur->OSTCBY;        /* Delay current task                                 */
        OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;
        if (OSRdyTbl[y] == 0u) {
            OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;
        }
        OSTCBCur->OSTCBDly = ticks;              /* Load ticks in TCB                                  */
        OS_EXIT_CRITICAL();
        OS_Sched();                              /* Find next task to run!                             */
    }

上面那一句代碼的意思是,記錄當前任務需要的延時時間(后面講具體的實現機制),比如當我調用delay_ms(5000),那樣記錄的就是5000,但我調用delay_ms(100)記錄的就是100,只不過單位可能不同,我這里寫的毫秒,是因為我在系統的配置文件中把系統的節拍設置成了毫秒:

#define OS_TICKS_PER_SEC           1000u   /* Set the number of ticks in one second                        */

這個宏定義系統的滴答定時器,每一秒鍾發生多少個節拍,我定義的是1000,所以每個節拍就是1ms,因此延時函數也是這個級別(其實這樣不太好,因為節拍太密集,系統的負荷會很重,實際肯定會根據需要調整成稍微大一些的數)。

以上,進入延時函數的任務切換中,把當前任務的就緒狀態設置為未就緒的流程就講解完畢了,接下來講解如何喚醒一個新的任務。

待續……

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM