繼續……
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,因此延時函數也是這個級別(其實這樣不太好,因為節拍太密集,系統的負荷會很重,實際肯定會根據需要調整成稍微大一些的數)。
以上,進入延時函數的任務切換中,把當前任務的就緒狀態設置為未就緒的流程就講解完畢了,接下來講解如何喚醒一個新的任務。
待續……