uC/OS-II將任務控制塊分成兩個鏈表來管理,這就是空閑任務鏈表和就緒任務鏈表。其中,空閑任務鏈表包含了所有空閑的任務控制塊。所謂空閑任務控制塊,是指未分配給某個任務的任務控制塊。創建一個新任務,前提條件就是系統中還有這樣的空閑任務塊。就緒鏈表則是將所有的就緒任務拴在一起,如果有新的任務就緒,就要將其任務控制塊從空閑鏈表中取出,加入到就緒鏈表中。
操作系統剛啟動的時候,在沒有執行主程序(Main)任何代碼之前,只有任務控制塊數組,還沒有空閑任務鏈表和就緒任務鏈表,或者說這兩個鏈表都空著。這兩個鏈表是在操作系統的初始化程序OSInit中創建的。如下圖所示為系統初始化程序OSInit執行后的空閑任務控制塊鏈表。
如圖可見,全局變量OSTCBFreeList指向的是空閑鏈表的表頭,表尾的任務控制塊的OSTCBNext指向的是空地址,也就是說,如果OSTCBFreeList指向的是空地址,就沒有空閑的任務控制塊,不能創建新的任務。
既然OSTCBFreeList是指向任務控制塊的地址,定義如下:
就是這樣,指向任務控制塊的指針。因為它與硬件無關,所以在ucos_ii.h中定義。
如果OS_MAX_TASKS的值是14,也就是最多可以同時運行14個用戶任務,系統任務數是2,那么內存中就有16個任務控制塊,從OSTCBTbl[0]到OSTCBTbl[15]。在操作系統沒有建立任何任務的時候,OSTCBFreeList指向OSTCBTbl[0], OSTCBTbl[0]的OSTCBNext指向OSTCBTbl[1],依此類推。最后,OSTCBTbl[15]的OSTCBNext指向空地址。如果要創建一個新的任務,就根據OSTCBFreeList找到一個任務控制塊。
當我們創建第一個任務即空閑任務的時候,就從空閑控制塊鏈表中摘下一個任務控制塊給這個任務。當然,這個控制塊隨即被插入就緒鏈表。很明顯,在沒有任務被創建的時候,就緒鏈表是空的。就緒鏈表的指針式OS_TCBList,它和OSTCBFreeList的定義方法是完全相同的:
第一個任務應該是空閑任務,如下圖所示就是完成了這一操作后的空閑鏈表和就緒鏈表。
當創建第一個任務即空閑任務后,空閑鏈表和就緒鏈表如上圖所示,這時可以清楚地看到就緒鏈表包含了OSTCBTbl[0]一個控制塊,OSTCBList就只想這個任務控制塊,而該控制塊的前后指針都為0,即OSTCBNext=0,OSTCBPrev=0,表示前面和后面都沒有了,就緒鏈表只有這一個塊。比較一下,空閑鏈表的指針OSTCBFreeList現在指向OSTCBTbl[1]了,空閑鏈表比原來少了一個控制塊,OSTCBTbl[0]被分配了。
創建一個任務的過程,首先應該查看OSTCBFreeList,看它是否為0,如果為0,說明沒有空閑的任務控制塊可以分配了,于是不能創建新的任務。如果不是0,就將OSTCBFreeList指向的那個任務控制塊分配給新的任務,將這個控制塊移動到就緒鏈表,并將OSTCBFreeList指向原來的空閑鏈表中的下一個任務控制塊。
那么,如何將任務控制塊移動到就緒鏈表呢?是插入到表頭還是表尾呢?
在上圖的基礎上我們在創建一個任務——統計任務。需要再從空閑鏈表中移出一個任務控制塊到就緒鏈表中,結果如下圖所示:
當再創建一個任務即統計任務后,OSTCBFreeList所指向的任務控制塊OSTCBTbl[1]就被分配給這個任務,OSTCBFreeList就指向了OSTCBTbl[2]。形成如上圖所示的右半部分新的空閑鏈表。uC/OS-II將這個OSTCBTbl[2]插入就緒鏈表,方法是把它放在表頭而不是表尾,于是形成了如圖所示的左半部分新的就緒鏈表,原來在這個鏈表中OSTCBTbl[0]就到了表尾。這時候,OS_TCBList不再指向OSTCBTbl[0],而是指向OSTCBTbl[1]。需要注意的是,就緒鏈表是雙向鏈表,而空閑鏈表只是一個單項鏈表,從圖中很容易分辨這一點。
從圖中可以清楚地看出,OSTCBFreeList永遠指向空閑鏈表的表頭,如果它為0,說明沒有空閑任務控制塊了;OSTCBList永遠指向就緒鏈表的表頭,如果它為0,說明沒有就緒的任務了。
|